Merge branch 'main' into fix-version-inline
This commit is contained in:
@@ -71,7 +71,8 @@ RUN --mount=type=secret,id=VITE_CORS_PROXY_URL \
|
||||
npm run build:with-docs
|
||||
|
||||
# Production stage
|
||||
FROM quay.io/nginx/nginx-unprivileged:stable-alpine-slim
|
||||
# TODO@ALAM: Change to quay/nginxinc-unprivileged once 1.28.3 is available
|
||||
FROM nginx:1.28.3-alpine-slim
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/alam00000/bentopdf"
|
||||
LABEL org.opencontainers.image.url="https://github.com/alam00000/bentopdf"
|
||||
@@ -82,14 +83,14 @@ ARG BASE_URL
|
||||
# Set this to "true" to disable Nginx listening on IPv6
|
||||
ENV DISABLE_IPV6=false
|
||||
|
||||
USER root
|
||||
RUN apk upgrade --no-cache
|
||||
USER nginx
|
||||
|
||||
COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html${BASE_URL%/}
|
||||
COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --chown=nginx:nginx --chmod=755 nginx-ipv6.sh /docker-entrypoint.d/99-disable-ipv6.sh
|
||||
RUN mkdir -p /etc/nginx/tmp && chown -R nginx:nginx /etc/nginx/tmp
|
||||
RUN mkdir -p /etc/nginx/tmp /var/cache/nginx && chown -R nginx:nginx /etc/nginx /var/cache/nginx
|
||||
|
||||
USER nginx
|
||||
|
||||
EXPOSE 8080
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -64,7 +64,7 @@ RUN --mount=type=secret,id=VITE_CORS_PROXY_URL \
|
||||
npm run build:with-docs
|
||||
|
||||
# Production stage — uses standard nginx (starts as root, drops to PUID/PGID)
|
||||
FROM nginx:stable-alpine-slim
|
||||
FROM nginx:1.28.3-alpine-slim
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/alam00000/bentopdf"
|
||||
LABEL org.opencontainers.image.url="https://github.com/alam00000/bentopdf"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bento-pdf",
|
||||
"private": true,
|
||||
"version": "2.7.0",
|
||||
"version": "2.8.1",
|
||||
"license": "AGPL-3.0-only",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "فشل تحميل القالب.",
|
||||
"noSettings": "لا توجد إعدادات قابلة للتخصيص لهذه العقدة.",
|
||||
"advancedSettings": "إعدادات متقدمة"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "تراكب PDF",
|
||||
"subtitle": "وضع صفحات من ملف PDF فوق أو تحت صفحات ملف PDF آخر.",
|
||||
"basePdfLabel": "ملف PDF الأساسي",
|
||||
"uploadBasePdf": "رفع ملف PDF الأساسي",
|
||||
"overlayPdfLabel": "ملف PDF للتراكب / التبطين",
|
||||
"uploadOverlayPdf": "رفع ملف PDF للتراكب/التبطين",
|
||||
"modeLabel": "الوضع",
|
||||
"overlayOption": "تراكب (فوق الصفحات)",
|
||||
"underlayOption": "تبطين (خلف الصفحات)",
|
||||
"pageRangeLabel": "تطبيق على الصفحات (اختياري)",
|
||||
"pageRangeHint": "استخدم \"z\" للصفحة الأخيرة. اتركه فارغاً للتطبيق على جميع الصفحات.",
|
||||
"repeatLabel": "تكرار صفحات التراكب/التبطين إذا كان الملف الأساسي أطول",
|
||||
"processButton": "تطبيق التراكب / التبطين",
|
||||
"howItWorksUploadTitle": "رفع ملفي PDF",
|
||||
"howItWorksUploadDescription": "ارفع ملف PDF الأساسي والملف الذي تريد تراكبه أو تبطينه.",
|
||||
"howItWorksModeTitle": "اختر الوضع",
|
||||
"howItWorksModeDescription": "اختر التراكب (فوق) أو التبطين (خلف). يمكنك تحديد نطاق صفحات والتكرار اختيارياً.",
|
||||
"howItWorksDownloadTitle": "تنزيل",
|
||||
"howItWorksDownloadDescription": "قم بتنزيل ملف PDF مع التراكب أو التبطين المطبق.",
|
||||
"faqDifferenceQuestion": "ما الفرق بين التراكب والتبطين؟",
|
||||
"faqDifferenceAnswer": "التراكب يضع الصفحات فوق ملف PDF الأساسي (مثل العلامة المائية). التبطين يضع الصفحات خلف ملف PDF الأساسي (مثل الترويسة أو الخلفية).",
|
||||
"faqSinglePageQuestion": "هل يمكنني استخدام ملف PDF من صفحة واحدة كتراكب لجميع الصفحات؟",
|
||||
"faqSinglePageAnswer": "نعم! فعّل خيار \"التكرار\" وستتكرر صفحات التراكب على جميع صفحات الملف الأساسي. العلامة المائية ذات الصفحة الواحدة ستُطبق على كل صفحة.",
|
||||
"faqPrivacyQuestion": "هل ملفاتي خاصة وآمنة؟",
|
||||
"faqPrivacyAnswer": "بالتأكيد! تتم جميع المعالجة في متصفحك. لا تغادر ملفاتك جهازك أبداً، مما يضمن خصوصية كاملة."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Не ўдалося загрузіць шаблон.",
|
||||
"noSettings": "Няма налад для гэтага вузла.",
|
||||
"advancedSettings": "Пашыраныя налады"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Накладванне PDF",
|
||||
"subtitle": "Накладваць або падкладваць старонкі аднаго PDF на іншы.",
|
||||
"basePdfLabel": "Базавы PDF",
|
||||
"uploadBasePdf": "Загрузіць базавы PDF",
|
||||
"overlayPdfLabel": "PDF для накладання / падкладання",
|
||||
"uploadOverlayPdf": "Загрузіць PDF для накладання/падкладання",
|
||||
"modeLabel": "Рэжым",
|
||||
"overlayOption": "Накладанне (зверху старонак)",
|
||||
"underlayOption": "Падкладанне (за старонкамі)",
|
||||
"pageRangeLabel": "Прымяніць да старонак (неабавязкова)",
|
||||
"pageRangeHint": "Выкарыстоўвайце \"z\" для апошняй старонкі. Пакіньце пустым для прымянення да ўсіх старонак.",
|
||||
"repeatLabel": "Паўтараць старонкі накладання/падкладання, калі базавы даўжэй",
|
||||
"processButton": "Прымяніць накладанне / падкладанне",
|
||||
"howItWorksUploadTitle": "Загрузіце два PDF",
|
||||
"howItWorksUploadDescription": "Загрузіце базавы PDF і PDF, які хочаце накласці або падкласці.",
|
||||
"howItWorksModeTitle": "Абярыце рэжым",
|
||||
"howItWorksModeDescription": "Абярыце накладанне (зверху) або падкладанне (ззаду). Можна ўсталяваць дыяпазон старонак і паўтор.",
|
||||
"howItWorksDownloadTitle": "Спампаваць",
|
||||
"howItWorksDownloadDescription": "Спампуйце PDF з прымененым накладаннем або падкладаннем.",
|
||||
"faqDifferenceQuestion": "У чым розніца паміж накладаннем і падкладаннем?",
|
||||
"faqDifferenceAnswer": "Накладанне размяшчае старонкі зверху базавага PDF (як вадзяны знак). Падкладанне размяшчае старонкі за базавым PDF (як фірмовы бланк або фон).",
|
||||
"faqSinglePageQuestion": "Ці магу я выкарыстоўваць аднастаронкавы PDF як накладанне для ўсіх старонак?",
|
||||
"faqSinglePageAnswer": "Так! Уключыце опцыю \"Паўтор\" і старонкі накладання будуць паўтарацца на ўсіх старонках базавага файла. Аднастаронкавы вадзяны знак будзе прымяняцца да кожнай старонкі.",
|
||||
"faqPrivacyQuestion": "Ці з'яўляюцца мае файлы прыватнымі і бяспечнымі?",
|
||||
"faqPrivacyAnswer": "Безумоўна! Уся апрацоўка адбываецца ў вашым браўзеры. Вашы файлы ніколі не пакідаюць вашу прыладу, забяспечваючы поўную прыватнасць."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Kunne ikke indlæse skabelon.",
|
||||
"noSettings": "Ingen konfigurerbare indstillinger for denne node.",
|
||||
"advancedSettings": "Avancerede indstillinger"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF-overlejring",
|
||||
"subtitle": "Læg sider fra én PDF oven på eller under sider i en anden PDF.",
|
||||
"basePdfLabel": "Basis-PDF",
|
||||
"uploadBasePdf": "Upload basis-PDF",
|
||||
"overlayPdfLabel": "Overlejring / Underlag PDF",
|
||||
"uploadOverlayPdf": "Upload overlejring/underlag PDF",
|
||||
"modeLabel": "Tilstand",
|
||||
"overlayOption": "Overlejring (oven på sider)",
|
||||
"underlayOption": "Underlag (bag sider)",
|
||||
"pageRangeLabel": "Anvend på sider (valgfrit)",
|
||||
"pageRangeHint": "Brug \"z\" for den sidste side. Lad feltet stå tomt for at anvende på alle sider.",
|
||||
"repeatLabel": "Gentag overlejring/underlag-sider hvis basis er længere",
|
||||
"processButton": "Anvend overlejring / underlag",
|
||||
"howItWorksUploadTitle": "Upload to PDF'er",
|
||||
"howItWorksUploadDescription": "Upload din basis-PDF og den PDF, du vil overlejre eller underlægge.",
|
||||
"howItWorksModeTitle": "Vælg tilstand",
|
||||
"howItWorksModeDescription": "Vælg overlejring (oven på) eller underlag (bagved). Indstil eventuelt et sideinterval og gentagelse.",
|
||||
"howItWorksDownloadTitle": "Download",
|
||||
"howItWorksDownloadDescription": "Download din PDF med overlejring eller underlag anvendt.",
|
||||
"faqDifferenceQuestion": "Hvad er forskellen mellem overlejring og underlag?",
|
||||
"faqDifferenceAnswer": "Overlejring placerer sider oven på din basis-PDF (som et vandmærke). Underlag placerer sider bag din basis-PDF (som brevhoved eller baggrund).",
|
||||
"faqSinglePageQuestion": "Kan jeg bruge en enkeltsidet PDF som overlejring på alle sider?",
|
||||
"faqSinglePageAnswer": "Ja! Aktivér \"Gentag\"-indstillingen, og dine overlejringssider vil gentage sig på alle basis-sider. Et enkeltsidigt vandmærke vil blive anvendt på hver side.",
|
||||
"faqPrivacyQuestion": "Er mine filer private og sikre?",
|
||||
"faqPrivacyAnswer": "Absolut! Al behandling sker i din browser. Dine filer forlader aldrig din enhed, hvilket sikrer fuldstændig privatliv."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,5 +823,32 @@
|
||||
"failedLoadTemplate": "Vorlage konnte nicht geladen werden.",
|
||||
"noSettings": "Keine konfigurierbaren Einstellungen für diesen Node.",
|
||||
"advancedSettings": "Erweiterte Einstellungen"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF-Überlagerung",
|
||||
"subtitle": "Seiten eines PDFs über oder unter die Seiten eines anderen PDFs legen.",
|
||||
"basePdfLabel": "Basis-PDF",
|
||||
"uploadBasePdf": "Basis-PDF hochladen",
|
||||
"overlayPdfLabel": "Überlagerungs- / Unterlagerungs-PDF",
|
||||
"uploadOverlayPdf": "Überlagerungs-/Unterlagerungs-PDF hochladen",
|
||||
"modeLabel": "Modus",
|
||||
"overlayOption": "Überlagerung (über den Seiten)",
|
||||
"underlayOption": "Unterlagerung (hinter den Seiten)",
|
||||
"pageRangeLabel": "Auf Seiten anwenden (optional)",
|
||||
"pageRangeHint": "Verwenden Sie \"z\" für die letzte Seite. Leer lassen, um auf alle Seiten anzuwenden.",
|
||||
"repeatLabel": "Überlagerungs-/Unterlagerungsseiten wiederholen, wenn Basis länger ist",
|
||||
"processButton": "Überlagerung / Unterlagerung anwenden",
|
||||
"howItWorksUploadTitle": "Zwei PDFs hochladen",
|
||||
"howItWorksUploadDescription": "Laden Sie Ihr Basis-PDF und das PDF hoch, das Sie überlagern oder unterlagern möchten.",
|
||||
"howItWorksModeTitle": "Modus wählen",
|
||||
"howItWorksModeDescription": "Wählen Sie Überlagerung (darüber) oder Unterlagerung (dahinter). Optional Seitenbereich und Wiederholung festlegen.",
|
||||
"howItWorksDownloadTitle": "Herunterladen",
|
||||
"howItWorksDownloadDescription": "Laden Sie Ihr PDF mit angewandter Überlagerung oder Unterlagerung herunter.",
|
||||
"faqDifferenceQuestion": "Was ist der Unterschied zwischen Überlagerung und Unterlagerung?",
|
||||
"faqDifferenceAnswer": "Überlagerung platziert Seiten über Ihr Basis-PDF (wie ein Wasserzeichen). Unterlagerung platziert Seiten hinter Ihr Basis-PDF (wie ein Briefkopf oder Hintergrund).",
|
||||
"faqSinglePageQuestion": "Kann ich ein einseitiges PDF als Überlagerung für alle Seiten verwenden?",
|
||||
"faqSinglePageAnswer": "Ja! Aktivieren Sie die Option \"Wiederholen\" und Ihre Überlagerungsseiten werden auf allen Basisseiten wiederholt. Ein einseitiges Wasserzeichen wird auf jeder Seite angewendet.",
|
||||
"faqPrivacyQuestion": "Sind meine Dateien privat und sicher?",
|
||||
"faqPrivacyAnswer": "Absolut! Die gesamte Verarbeitung findet in Ihrem Browser statt. Ihre Dateien verlassen niemals Ihr Gerät, was vollständige Privatsphäre gewährleistet."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,5 +831,32 @@
|
||||
"failedLoadTemplate": "Failed to load template.",
|
||||
"noSettings": "No configurable settings for this node.",
|
||||
"advancedSettings": "Advanced Settings"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF Overlay",
|
||||
"subtitle": "Overlay or underlay pages from one PDF onto another.",
|
||||
"basePdfLabel": "Base PDF",
|
||||
"uploadBasePdf": "Upload base PDF",
|
||||
"overlayPdfLabel": "Overlay / Underlay PDF",
|
||||
"uploadOverlayPdf": "Upload overlay/underlay PDF",
|
||||
"modeLabel": "Mode",
|
||||
"overlayOption": "Overlay (on top of pages)",
|
||||
"underlayOption": "Underlay (behind pages)",
|
||||
"pageRangeLabel": "Apply to pages (optional)",
|
||||
"pageRangeHint": "Use \"z\" for the last page. Leave empty to apply to all pages.",
|
||||
"repeatLabel": "Loop overlay/underlay pages if base is longer",
|
||||
"processButton": "Apply Overlay / Underlay",
|
||||
"howItWorksUploadTitle": "Upload Two PDFs",
|
||||
"howItWorksUploadDescription": "Upload your base PDF and the PDF you want to overlay or underlay.",
|
||||
"howItWorksModeTitle": "Choose Mode",
|
||||
"howItWorksModeDescription": "Select overlay (on top) or underlay (behind). Optionally set a page range and repeat.",
|
||||
"howItWorksDownloadTitle": "Download",
|
||||
"howItWorksDownloadDescription": "Download your PDF with the overlay or underlay applied.",
|
||||
"faqDifferenceQuestion": "What is the difference between overlay and underlay?",
|
||||
"faqDifferenceAnswer": "Overlay places pages on top of your base PDF (like a watermark). Underlay places pages behind your base PDF (like a letterhead or background).",
|
||||
"faqSinglePageQuestion": "Can I use a single-page PDF as overlay for all pages?",
|
||||
"faqSinglePageAnswer": "Yes! Enable the \"Loop\" option and your overlay PDF pages will repeat across all base pages. A single-page watermark will apply to every page.",
|
||||
"faqPrivacyQuestion": "Are my files private and secure?",
|
||||
"faqPrivacyAnswer": "Absolutely! All processing happens in your browser. Your files never leave your device, ensuring complete privacy."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Error al cargar la plantilla.",
|
||||
"noSettings": "No hay opciones configurables para este nodo.",
|
||||
"advancedSettings": "Configuración avanzada"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Superposición de PDF",
|
||||
"subtitle": "Superponer o colocar como fondo páginas de un PDF sobre otro.",
|
||||
"basePdfLabel": "PDF base",
|
||||
"uploadBasePdf": "Subir PDF base",
|
||||
"overlayPdfLabel": "PDF de superposición / fondo",
|
||||
"uploadOverlayPdf": "Subir PDF de superposición/fondo",
|
||||
"modeLabel": "Modo",
|
||||
"overlayOption": "Superposición (encima de las páginas)",
|
||||
"underlayOption": "Fondo (detrás de las páginas)",
|
||||
"pageRangeLabel": "Aplicar a páginas (opcional)",
|
||||
"pageRangeHint": "Use \"z\" para la última página. Deje vacío para aplicar a todas las páginas.",
|
||||
"repeatLabel": "Repetir páginas de superposición/fondo si el base es más largo",
|
||||
"processButton": "Aplicar superposición / fondo",
|
||||
"howItWorksUploadTitle": "Subir dos PDFs",
|
||||
"howItWorksUploadDescription": "Suba su PDF base y el PDF que desea superponer o colocar como fondo.",
|
||||
"howItWorksModeTitle": "Elegir modo",
|
||||
"howItWorksModeDescription": "Seleccione superposición (encima) o fondo (detrás). Opcionalmente establezca un rango de páginas y repetición.",
|
||||
"howItWorksDownloadTitle": "Descargar",
|
||||
"howItWorksDownloadDescription": "Descargue su PDF con la superposición o fondo aplicado.",
|
||||
"faqDifferenceQuestion": "¿Cuál es la diferencia entre superposición y fondo?",
|
||||
"faqDifferenceAnswer": "La superposición coloca páginas encima de su PDF base (como una marca de agua). El fondo coloca páginas detrás de su PDF base (como un membrete o fondo).",
|
||||
"faqSinglePageQuestion": "¿Puedo usar un PDF de una sola página como superposición para todas las páginas?",
|
||||
"faqSinglePageAnswer": "¡Sí! Active la opción \"Repetir\" y las páginas de superposición se repetirán en todas las páginas base. Una marca de agua de una página se aplicará a cada página.",
|
||||
"faqPrivacyQuestion": "¿Mis archivos son privados y seguros?",
|
||||
"faqPrivacyAnswer": "¡Por supuesto! Todo el procesamiento ocurre en su navegador. Sus archivos nunca abandonan su dispositivo, garantizando total privacidad."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Échec du chargement du modèle.",
|
||||
"noSettings": "Aucun paramètre configurable pour ce nœud.",
|
||||
"advancedSettings": "Paramètres avancés"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Superposition PDF",
|
||||
"subtitle": "Superposer ou placer en arrière-plan les pages d'un PDF sur un autre.",
|
||||
"basePdfLabel": "PDF de base",
|
||||
"uploadBasePdf": "Télécharger le PDF de base",
|
||||
"overlayPdfLabel": "PDF de superposition / arrière-plan",
|
||||
"uploadOverlayPdf": "Télécharger le PDF de superposition/arrière-plan",
|
||||
"modeLabel": "Mode",
|
||||
"overlayOption": "Superposition (au-dessus des pages)",
|
||||
"underlayOption": "Arrière-plan (derrière les pages)",
|
||||
"pageRangeLabel": "Appliquer aux pages (optionnel)",
|
||||
"pageRangeHint": "Utilisez \"z\" pour la dernière page. Laissez vide pour appliquer à toutes les pages.",
|
||||
"repeatLabel": "Répéter les pages de superposition/arrière-plan si la base est plus longue",
|
||||
"processButton": "Appliquer superposition / arrière-plan",
|
||||
"howItWorksUploadTitle": "Télécharger deux PDFs",
|
||||
"howItWorksUploadDescription": "Téléchargez votre PDF de base et le PDF que vous souhaitez superposer ou placer en arrière-plan.",
|
||||
"howItWorksModeTitle": "Choisir le mode",
|
||||
"howItWorksModeDescription": "Sélectionnez superposition (au-dessus) ou arrière-plan (derrière). Définissez optionnellement une plage de pages et la répétition.",
|
||||
"howItWorksDownloadTitle": "Télécharger",
|
||||
"howItWorksDownloadDescription": "Téléchargez votre PDF avec la superposition ou l'arrière-plan appliqué.",
|
||||
"faqDifferenceQuestion": "Quelle est la différence entre superposition et arrière-plan ?",
|
||||
"faqDifferenceAnswer": "La superposition place les pages au-dessus de votre PDF de base (comme un filigrane). L'arrière-plan place les pages derrière votre PDF de base (comme un en-tête ou un fond).",
|
||||
"faqSinglePageQuestion": "Puis-je utiliser un PDF d'une seule page comme superposition pour toutes les pages ?",
|
||||
"faqSinglePageAnswer": "Oui ! Activez l'option \"Répéter\" et vos pages de superposition se répéteront sur toutes les pages de base. Un filigrane d'une seule page s'appliquera à chaque page.",
|
||||
"faqPrivacyQuestion": "Mes fichiers sont-ils privés et sécurisés ?",
|
||||
"faqPrivacyAnswer": "Absolument ! Tout le traitement se fait dans votre navigateur. Vos fichiers ne quittent jamais votre appareil, garantissant une confidentialité totale."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Gagal memuat template.",
|
||||
"noSettings": "Tidak ada pengaturan yang dapat dikonfigurasi untuk node ini.",
|
||||
"advancedSettings": "Pengaturan Lanjutan"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Overlay PDF",
|
||||
"subtitle": "Tumpangkan atau sisipkan halaman dari satu PDF ke PDF lainnya.",
|
||||
"basePdfLabel": "PDF Dasar",
|
||||
"uploadBasePdf": "Unggah PDF dasar",
|
||||
"overlayPdfLabel": "PDF Overlay / Underlay",
|
||||
"uploadOverlayPdf": "Unggah PDF overlay/underlay",
|
||||
"modeLabel": "Mode",
|
||||
"overlayOption": "Overlay (di atas halaman)",
|
||||
"underlayOption": "Underlay (di belakang halaman)",
|
||||
"pageRangeLabel": "Terapkan ke halaman (opsional)",
|
||||
"pageRangeHint": "Gunakan \"z\" untuk halaman terakhir. Kosongkan untuk menerapkan ke semua halaman.",
|
||||
"repeatLabel": "Ulangi halaman overlay/underlay jika dasar lebih panjang",
|
||||
"processButton": "Terapkan Overlay / Underlay",
|
||||
"howItWorksUploadTitle": "Unggah Dua PDF",
|
||||
"howItWorksUploadDescription": "Unggah PDF dasar Anda dan PDF yang ingin Anda tumpangkan atau sisipkan.",
|
||||
"howItWorksModeTitle": "Pilih Mode",
|
||||
"howItWorksModeDescription": "Pilih overlay (di atas) atau underlay (di belakang). Atur rentang halaman dan pengulangan secara opsional.",
|
||||
"howItWorksDownloadTitle": "Unduh",
|
||||
"howItWorksDownloadDescription": "Unduh PDF Anda dengan overlay atau underlay yang diterapkan.",
|
||||
"faqDifferenceQuestion": "Apa perbedaan antara overlay dan underlay?",
|
||||
"faqDifferenceAnswer": "Overlay menempatkan halaman di atas PDF dasar Anda (seperti watermark). Underlay menempatkan halaman di belakang PDF dasar Anda (seperti kop surat atau latar belakang).",
|
||||
"faqSinglePageQuestion": "Bisakah saya menggunakan PDF satu halaman sebagai overlay untuk semua halaman?",
|
||||
"faqSinglePageAnswer": "Ya! Aktifkan opsi \"Ulangi\" dan halaman overlay Anda akan berulang di semua halaman dasar. Watermark satu halaman akan diterapkan ke setiap halaman.",
|
||||
"faqPrivacyQuestion": "Apakah file saya pribadi dan aman?",
|
||||
"faqPrivacyAnswer": "Tentu saja! Semua pemrosesan terjadi di browser Anda. File Anda tidak pernah meninggalkan perangkat Anda, memastikan privasi penuh."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Impossibile caricare il modello.",
|
||||
"noSettings": "Nessuna impostazione configurabile per questo nodo.",
|
||||
"advancedSettings": "Impostazioni avanzate"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Sovrapposizione PDF",
|
||||
"subtitle": "Sovrapponi o posiziona in sottofondo le pagine di un PDF su un altro.",
|
||||
"basePdfLabel": "PDF di base",
|
||||
"uploadBasePdf": "Carica PDF di base",
|
||||
"overlayPdfLabel": "PDF di sovrapposizione / sottofondo",
|
||||
"uploadOverlayPdf": "Carica PDF di sovrapposizione/sottofondo",
|
||||
"modeLabel": "Modalità",
|
||||
"overlayOption": "Sovrapposizione (sopra le pagine)",
|
||||
"underlayOption": "Sottofondo (dietro le pagine)",
|
||||
"pageRangeLabel": "Applica alle pagine (opzionale)",
|
||||
"pageRangeHint": "Usa \"z\" per l'ultima pagina. Lascia vuoto per applicare a tutte le pagine.",
|
||||
"repeatLabel": "Ripeti le pagine di sovrapposizione/sottofondo se la base è più lunga",
|
||||
"processButton": "Applica sovrapposizione / sottofondo",
|
||||
"howItWorksUploadTitle": "Carica due PDF",
|
||||
"howItWorksUploadDescription": "Carica il tuo PDF di base e il PDF che vuoi sovrapporre o mettere in sottofondo.",
|
||||
"howItWorksModeTitle": "Scegli la modalità",
|
||||
"howItWorksModeDescription": "Seleziona sovrapposizione (sopra) o sottofondo (dietro). Imposta facoltativamente un intervallo di pagine e la ripetizione.",
|
||||
"howItWorksDownloadTitle": "Scarica",
|
||||
"howItWorksDownloadDescription": "Scarica il tuo PDF con la sovrapposizione o il sottofondo applicato.",
|
||||
"faqDifferenceQuestion": "Qual è la differenza tra sovrapposizione e sottofondo?",
|
||||
"faqDifferenceAnswer": "La sovrapposizione posiziona le pagine sopra il PDF di base (come una filigrana). Il sottofondo posiziona le pagine dietro il PDF di base (come un'intestazione o uno sfondo).",
|
||||
"faqSinglePageQuestion": "Posso usare un PDF di una sola pagina come sovrapposizione per tutte le pagine?",
|
||||
"faqSinglePageAnswer": "Sì! Attiva l'opzione \"Ripeti\" e le pagine di sovrapposizione si ripeteranno su tutte le pagine base. Una filigrana di una pagina verrà applicata a ogni pagina.",
|
||||
"faqPrivacyQuestion": "I miei file sono privati e sicuri?",
|
||||
"faqPrivacyAnswer": "Assolutamente! Tutta l'elaborazione avviene nel tuo browser. I tuoi file non lasciano mai il tuo dispositivo, garantendo la massima privacy."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,5 +764,32 @@
|
||||
"failedLoadTemplate": "템플릿을 불러오지 못했습니다.",
|
||||
"noSettings": "이 노드에는 설정할 항목이 없습니다.",
|
||||
"advancedSettings": "고급 설정"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF 오버레이",
|
||||
"subtitle": "한 PDF의 페이지를 다른 PDF 위 또는 아래에 겹칩니다.",
|
||||
"basePdfLabel": "기본 PDF",
|
||||
"uploadBasePdf": "기본 PDF 업로드",
|
||||
"overlayPdfLabel": "오버레이 / 언더레이 PDF",
|
||||
"uploadOverlayPdf": "오버레이/언더레이 PDF 업로드",
|
||||
"modeLabel": "모드",
|
||||
"overlayOption": "오버레이 (페이지 위에)",
|
||||
"underlayOption": "언더레이 (페이지 뒤에)",
|
||||
"pageRangeLabel": "페이지에 적용 (선택사항)",
|
||||
"pageRangeHint": "마지막 페이지는 \"z\"를 사용하세요. 모든 페이지에 적용하려면 비워두세요.",
|
||||
"repeatLabel": "기본 문서가 더 길면 오버레이/언더레이 페이지 반복",
|
||||
"processButton": "오버레이 / 언더레이 적용",
|
||||
"howItWorksUploadTitle": "두 개의 PDF 업로드",
|
||||
"howItWorksUploadDescription": "기본 PDF와 오버레이 또는 언더레이할 PDF를 업로드하세요.",
|
||||
"howItWorksModeTitle": "모드 선택",
|
||||
"howItWorksModeDescription": "오버레이(위) 또는 언더레이(뒤)를 선택하세요. 선택적으로 페이지 범위와 반복을 설정할 수 있습니다.",
|
||||
"howItWorksDownloadTitle": "다운로드",
|
||||
"howItWorksDownloadDescription": "오버레이 또는 언더레이가 적용된 PDF를 다운로드하세요.",
|
||||
"faqDifferenceQuestion": "오버레이와 언더레이의 차이점은 무엇인가요?",
|
||||
"faqDifferenceAnswer": "오버레이는 기본 PDF 위에 페이지를 배치합니다(워터마크처럼). 언더레이는 기본 PDF 뒤에 페이지를 배치합니다(레터헤드나 배경처럼).",
|
||||
"faqSinglePageQuestion": "단일 페이지 PDF를 모든 페이지의 오버레이로 사용할 수 있나요?",
|
||||
"faqSinglePageAnswer": "네! \"반복\" 옵션을 활성화하면 오버레이 페이지가 모든 기본 페이지에 반복됩니다. 단일 페이지 워터마크가 모든 페이지에 적용됩니다.",
|
||||
"faqPrivacyQuestion": "내 파일은 비공개이고 안전한가요?",
|
||||
"faqPrivacyAnswer": "물론입니다! 모든 처리는 브라우저에서 이루어집니다. 파일이 기기를 떠나지 않아 완전한 개인정보 보호가 보장됩니다."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,5 +831,32 @@
|
||||
"failedLoadTemplate": "Sjabloon laden mislukt.",
|
||||
"noSettings": "Geen configureerbare instellingen voor deze node.",
|
||||
"advancedSettings": "Geavanceerde instellingen"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF-overlay",
|
||||
"subtitle": "Pagina's van een PDF over of onder een andere PDF plaatsen.",
|
||||
"basePdfLabel": "Basis-PDF",
|
||||
"uploadBasePdf": "Basis-PDF uploaden",
|
||||
"overlayPdfLabel": "Overlay / Underlay PDF",
|
||||
"uploadOverlayPdf": "Overlay/underlay PDF uploaden",
|
||||
"modeLabel": "Modus",
|
||||
"overlayOption": "Overlay (boven de pagina's)",
|
||||
"underlayOption": "Underlay (achter de pagina's)",
|
||||
"pageRangeLabel": "Toepassen op pagina's (optioneel)",
|
||||
"pageRangeHint": "Gebruik \"z\" voor de laatste pagina. Laat leeg om op alle pagina's toe te passen.",
|
||||
"repeatLabel": "Overlay/underlay-pagina's herhalen als basis langer is",
|
||||
"processButton": "Overlay / underlay toepassen",
|
||||
"howItWorksUploadTitle": "Twee PDF's uploaden",
|
||||
"howItWorksUploadDescription": "Upload uw basis-PDF en de PDF die u wilt overleggen of onderleggen.",
|
||||
"howItWorksModeTitle": "Kies modus",
|
||||
"howItWorksModeDescription": "Selecteer overlay (erboven) of underlay (erachter). Stel optioneel een paginabereik en herhaling in.",
|
||||
"howItWorksDownloadTitle": "Downloaden",
|
||||
"howItWorksDownloadDescription": "Download uw PDF met de overlay of underlay toegepast.",
|
||||
"faqDifferenceQuestion": "Wat is het verschil tussen overlay en underlay?",
|
||||
"faqDifferenceAnswer": "Overlay plaatst pagina's boven uw basis-PDF (zoals een watermerk). Underlay plaatst pagina's achter uw basis-PDF (zoals een briefhoofd of achtergrond).",
|
||||
"faqSinglePageQuestion": "Kan ik een enkele pagina PDF als overlay voor alle pagina's gebruiken?",
|
||||
"faqSinglePageAnswer": "Ja! Schakel de optie \"Herhalen\" in en uw overlay-pagina's worden herhaald over alle basispagina's. Een watermerk van één pagina wordt op elke pagina toegepast.",
|
||||
"faqPrivacyQuestion": "Zijn mijn bestanden privé en veilig?",
|
||||
"faqPrivacyAnswer": "Absoluut! Alle verwerking vindt plaats in uw browser. Uw bestanden verlaten nooit uw apparaat, wat volledige privacy garandeert."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Falha ao carregar o modelo.",
|
||||
"noSettings": "Nenhuma configuração disponível para este nó.",
|
||||
"advancedSettings": "Configurações avançadas"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Sobreposição de PDF",
|
||||
"subtitle": "Sobrepor ou subjacente páginas de um PDF em outro.",
|
||||
"basePdfLabel": "PDF base",
|
||||
"uploadBasePdf": "Carregar PDF base",
|
||||
"overlayPdfLabel": "PDF de sobreposição / fundo",
|
||||
"uploadOverlayPdf": "Carregar PDF de sobreposição/fundo",
|
||||
"modeLabel": "Modo",
|
||||
"overlayOption": "Sobreposição (acima das páginas)",
|
||||
"underlayOption": "Fundo (atrás das páginas)",
|
||||
"pageRangeLabel": "Aplicar às páginas (opcional)",
|
||||
"pageRangeHint": "Use \"z\" para a última página. Deixe vazio para aplicar a todas as páginas.",
|
||||
"repeatLabel": "Repetir páginas de sobreposição/fundo se a base for mais longa",
|
||||
"processButton": "Aplicar sobreposição / fundo",
|
||||
"howItWorksUploadTitle": "Carregar dois PDFs",
|
||||
"howItWorksUploadDescription": "Carregue o seu PDF base e o PDF que deseja sobrepor ou colocar como fundo.",
|
||||
"howItWorksModeTitle": "Escolher modo",
|
||||
"howItWorksModeDescription": "Selecione sobreposição (acima) ou fundo (atrás). Defina opcionalmente um intervalo de páginas e repetição.",
|
||||
"howItWorksDownloadTitle": "Baixar",
|
||||
"howItWorksDownloadDescription": "Baixe o seu PDF com a sobreposição ou fundo aplicado.",
|
||||
"faqDifferenceQuestion": "Qual é a diferença entre sobreposição e fundo?",
|
||||
"faqDifferenceAnswer": "A sobreposição coloca páginas acima do seu PDF base (como uma marca d'água). O fundo coloca páginas atrás do seu PDF base (como um cabeçalho ou fundo).",
|
||||
"faqSinglePageQuestion": "Posso usar um PDF de uma única página como sobreposição para todas as páginas?",
|
||||
"faqSinglePageAnswer": "Sim! Ative a opção \"Repetir\" e as páginas de sobreposição se repetirão em todas as páginas base. Uma marca d'água de uma página será aplicada a cada página.",
|
||||
"faqPrivacyQuestion": "Os meus ficheiros são privados e seguros?",
|
||||
"faqPrivacyAnswer": "Com certeza! Todo o processamento acontece no seu navegador. Os seus ficheiros nunca saem do seu dispositivo, garantindo total privacidade."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,5 +685,32 @@
|
||||
"rasterizePdf": {
|
||||
"name": "Растеризация документа",
|
||||
"subtitle": "Преобразовать содержимое страниц документа в изображения, свести слои и убрать выделяемый текст."
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Наложение PDF",
|
||||
"subtitle": "Наложить страницы одного PDF поверх или под страницы другого.",
|
||||
"basePdfLabel": "Базовый PDF",
|
||||
"uploadBasePdf": "Загрузить базовый PDF",
|
||||
"overlayPdfLabel": "PDF для наложения / подложки",
|
||||
"uploadOverlayPdf": "Загрузить PDF для наложения/подложки",
|
||||
"modeLabel": "Режим",
|
||||
"overlayOption": "Наложение (поверх страниц)",
|
||||
"underlayOption": "Подложка (за страницами)",
|
||||
"pageRangeLabel": "Применить к страницам (необязательно)",
|
||||
"pageRangeHint": "Используйте \"z\" для последней страницы. Оставьте пустым для применения ко всем страницам.",
|
||||
"repeatLabel": "Повторять страницы наложения/подложки, если базовый длиннее",
|
||||
"processButton": "Применить наложение / подложку",
|
||||
"howItWorksUploadTitle": "Загрузите два PDF",
|
||||
"howItWorksUploadDescription": "Загрузите базовый PDF и PDF, который хотите наложить или подложить.",
|
||||
"howItWorksModeTitle": "Выберите режим",
|
||||
"howItWorksModeDescription": "Выберите наложение (сверху) или подложку (сзади). При необходимости укажите диапазон страниц и повтор.",
|
||||
"howItWorksDownloadTitle": "Скачать",
|
||||
"howItWorksDownloadDescription": "Скачайте PDF с применённым наложением или подложкой.",
|
||||
"faqDifferenceQuestion": "В чём разница между наложением и подложкой?",
|
||||
"faqDifferenceAnswer": "Наложение размещает страницы поверх базового PDF (как водяной знак). Подложка размещает страницы за базовым PDF (как фирменный бланк или фон).",
|
||||
"faqSinglePageQuestion": "Можно ли использовать одностраничный PDF как наложение для всех страниц?",
|
||||
"faqSinglePageAnswer": "Да! Включите опцию \"Повтор\" и страницы наложения будут повторяться на всех страницах базового файла. Одностраничный водяной знак будет применён к каждой странице.",
|
||||
"faqPrivacyQuestion": "Мои файлы конфиденциальны и защищены?",
|
||||
"faqPrivacyAnswer": "Безусловно! Вся обработка происходит в вашем браузере. Ваши файлы никогда не покидают ваше устройство, обеспечивая полную конфиденциальность."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Det gick inte att ladda mallen.",
|
||||
"noSettings": "Inga konfigurerbara inställningar för denna nod.",
|
||||
"advancedSettings": "Avancerade inställningar"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF-överlägg",
|
||||
"subtitle": "Lägg sidor från en PDF ovanpå eller under en annan PDF.",
|
||||
"basePdfLabel": "Bas-PDF",
|
||||
"uploadBasePdf": "Ladda upp bas-PDF",
|
||||
"overlayPdfLabel": "Överlägg / Underlägg PDF",
|
||||
"uploadOverlayPdf": "Ladda upp överlägg/underlägg PDF",
|
||||
"modeLabel": "Läge",
|
||||
"overlayOption": "Överlägg (ovanpå sidor)",
|
||||
"underlayOption": "Underlägg (bakom sidor)",
|
||||
"pageRangeLabel": "Tillämpa på sidor (valfritt)",
|
||||
"pageRangeHint": "Använd \"z\" för sista sidan. Lämna tomt för att tillämpa på alla sidor.",
|
||||
"repeatLabel": "Upprepa överlägg/underlägg-sidor om basen är längre",
|
||||
"processButton": "Tillämpa överlägg / underlägg",
|
||||
"howItWorksUploadTitle": "Ladda upp två PDF:er",
|
||||
"howItWorksUploadDescription": "Ladda upp din bas-PDF och den PDF du vill lägga som överlägg eller underlägg.",
|
||||
"howItWorksModeTitle": "Välj läge",
|
||||
"howItWorksModeDescription": "Välj överlägg (ovanpå) eller underlägg (bakom). Ställ valfritt in sidintervall och upprepning.",
|
||||
"howItWorksDownloadTitle": "Ladda ner",
|
||||
"howItWorksDownloadDescription": "Ladda ner din PDF med överlägg eller underlägg tillämpat.",
|
||||
"faqDifferenceQuestion": "Vad är skillnaden mellan överlägg och underlägg?",
|
||||
"faqDifferenceAnswer": "Överlägg placerar sidor ovanpå din bas-PDF (som en vattenstämpel). Underlägg placerar sidor bakom din bas-PDF (som ett brevhuvud eller bakgrund).",
|
||||
"faqSinglePageQuestion": "Kan jag använda en ensidig PDF som överlägg för alla sidor?",
|
||||
"faqSinglePageAnswer": "Ja! Aktivera alternativet \"Upprepa\" och dina överläggsidor upprepas på alla bassidor. En ensidig vattenstämpel tillämpas på varje sida.",
|
||||
"faqPrivacyQuestion": "Är mina filer privata och säkra?",
|
||||
"faqPrivacyAnswer": "Absolut! All bearbetning sker i din webbläsare. Dina filer lämnar aldrig din enhet, vilket garanterar fullständig integritet."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Şablon yüklenemedi.",
|
||||
"noSettings": "Bu düğüm için yapılandırılabilir ayar yok.",
|
||||
"advancedSettings": "Gelişmiş Ayarlar"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF Katmanlama",
|
||||
"subtitle": "Bir PDF'in sayfalarını başka bir PDF'in üzerine veya altına yerleştirin.",
|
||||
"basePdfLabel": "Temel PDF",
|
||||
"uploadBasePdf": "Temel PDF yükle",
|
||||
"overlayPdfLabel": "Üst Katman / Alt Katman PDF",
|
||||
"uploadOverlayPdf": "Üst katman/alt katman PDF yükle",
|
||||
"modeLabel": "Mod",
|
||||
"overlayOption": "Üst katman (sayfaların üzerinde)",
|
||||
"underlayOption": "Alt katman (sayfaların arkasında)",
|
||||
"pageRangeLabel": "Sayfalara uygula (isteğe bağlı)",
|
||||
"pageRangeHint": "Son sayfa için \"z\" kullanın. Tüm sayfalara uygulamak için boş bırakın.",
|
||||
"repeatLabel": "Temel daha uzunsa üst/alt katman sayfalarını tekrarla",
|
||||
"processButton": "Üst Katman / Alt Katman Uygula",
|
||||
"howItWorksUploadTitle": "İki PDF Yükle",
|
||||
"howItWorksUploadDescription": "Temel PDF'inizi ve üst katman veya alt katman yapmak istediğiniz PDF'i yükleyin.",
|
||||
"howItWorksModeTitle": "Mod Seçin",
|
||||
"howItWorksModeDescription": "Üst katman (üzerine) veya alt katman (arkasına) seçin. İsteğe bağlı olarak sayfa aralığı ve tekrar ayarlayın.",
|
||||
"howItWorksDownloadTitle": "İndir",
|
||||
"howItWorksDownloadDescription": "Üst katman veya alt katman uygulanmış PDF'inizi indirin.",
|
||||
"faqDifferenceQuestion": "Üst katman ile alt katman arasındaki fark nedir?",
|
||||
"faqDifferenceAnswer": "Üst katman, sayfaları temel PDF'inizin üzerine yerleştirir (filigran gibi). Alt katman, sayfaları temel PDF'inizin arkasına yerleştirir (antetli kağıt veya arka plan gibi).",
|
||||
"faqSinglePageQuestion": "Tek sayfalık bir PDF'i tüm sayfalar için üst katman olarak kullanabilir miyim?",
|
||||
"faqSinglePageAnswer": "Evet! \"Tekrarla\" seçeneğini etkinleştirin ve üst katman sayfalarınız tüm temel sayfalarda tekrarlanır. Tek sayfalık bir filigran her sayfaya uygulanır.",
|
||||
"faqPrivacyQuestion": "Dosyalarım gizli ve güvende mi?",
|
||||
"faqPrivacyAnswer": "Kesinlikle! Tüm işleme tarayıcınızda gerçekleşir. Dosyalarınız cihazınızı asla terk etmez, tam gizlilik sağlanır."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "Không thể tải mẫu.",
|
||||
"noSettings": "Không có cài đặt nào có thể cấu hình cho nút này.",
|
||||
"advancedSettings": "Cài đặt nâng cao"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "Chồng PDF",
|
||||
"subtitle": "Chồng hoặc lót các trang từ một PDF lên PDF khác.",
|
||||
"basePdfLabel": "PDF gốc",
|
||||
"uploadBasePdf": "Tải lên PDF gốc",
|
||||
"overlayPdfLabel": "PDF chồng / lót",
|
||||
"uploadOverlayPdf": "Tải lên PDF chồng/lót",
|
||||
"modeLabel": "Chế độ",
|
||||
"overlayOption": "Chồng lên (trên các trang)",
|
||||
"underlayOption": "Lót dưới (sau các trang)",
|
||||
"pageRangeLabel": "Áp dụng cho trang (tùy chọn)",
|
||||
"pageRangeHint": "Dùng \"z\" cho trang cuối. Để trống để áp dụng cho tất cả các trang.",
|
||||
"repeatLabel": "Lặp lại các trang chồng/lót nếu tài liệu gốc dài hơn",
|
||||
"processButton": "Áp dụng chồng / lót",
|
||||
"howItWorksUploadTitle": "Tải lên hai PDF",
|
||||
"howItWorksUploadDescription": "Tải lên PDF gốc và PDF bạn muốn chồng hoặc lót.",
|
||||
"howItWorksModeTitle": "Chọn chế độ",
|
||||
"howItWorksModeDescription": "Chọn chồng lên (phía trên) hoặc lót dưới (phía sau). Tùy chọn đặt phạm vi trang và lặp lại.",
|
||||
"howItWorksDownloadTitle": "Tải xuống",
|
||||
"howItWorksDownloadDescription": "Tải xuống PDF của bạn với phần chồng hoặc lót đã được áp dụng.",
|
||||
"faqDifferenceQuestion": "Sự khác biệt giữa chồng lên và lót dưới là gì?",
|
||||
"faqDifferenceAnswer": "Chồng lên đặt các trang lên trên PDF gốc (như hình mờ). Lót dưới đặt các trang phía sau PDF gốc (như tiêu đề thư hoặc nền).",
|
||||
"faqSinglePageQuestion": "Tôi có thể dùng PDF một trang làm chồng cho tất cả các trang không?",
|
||||
"faqSinglePageAnswer": "Có! Bật tùy chọn \"Lặp lại\" và các trang chồng sẽ lặp lại trên tất cả các trang gốc. Hình mờ một trang sẽ được áp dụng cho mọi trang.",
|
||||
"faqPrivacyQuestion": "Tệp của tôi có được bảo mật không?",
|
||||
"faqPrivacyAnswer": "Chắc chắn! Toàn bộ quá trình xử lý diễn ra trong trình duyệt của bạn. Tệp của bạn không bao giờ rời khỏi thiết bị, đảm bảo quyền riêng tư hoàn toàn."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "載入範本失敗。",
|
||||
"noSettings": "此節點沒有可設定的選項。",
|
||||
"advancedSettings": "進階設定"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF 疊加",
|
||||
"subtitle": "將一個 PDF 的頁面疊加或墊於另一個 PDF 之上或之下。",
|
||||
"basePdfLabel": "基礎 PDF",
|
||||
"uploadBasePdf": "上傳基礎 PDF",
|
||||
"overlayPdfLabel": "疊加 / 襯底 PDF",
|
||||
"uploadOverlayPdf": "上傳疊加/襯底 PDF",
|
||||
"modeLabel": "模式",
|
||||
"overlayOption": "疊加(在頁面上方)",
|
||||
"underlayOption": "襯底(在頁面下方)",
|
||||
"pageRangeLabel": "套用到頁面(選填)",
|
||||
"pageRangeHint": "使用 \"z\" 表示最後一頁。留空則套用到所有頁面。",
|
||||
"repeatLabel": "如果基礎文件較長,則循環疊加/襯底頁面",
|
||||
"processButton": "套用疊加 / 襯底",
|
||||
"howItWorksUploadTitle": "上傳兩個 PDF",
|
||||
"howItWorksUploadDescription": "上傳您的基礎 PDF 和要疊加或襯底的 PDF。",
|
||||
"howItWorksModeTitle": "選擇模式",
|
||||
"howItWorksModeDescription": "選擇疊加(上方)或襯底(下方)。可選設定頁面範圍和重複。",
|
||||
"howItWorksDownloadTitle": "下載",
|
||||
"howItWorksDownloadDescription": "下載套用了疊加或襯底的 PDF。",
|
||||
"faqDifferenceQuestion": "疊加和襯底有什麼區別?",
|
||||
"faqDifferenceAnswer": "疊加將頁面放在基礎 PDF 上方(如浮水印)。襯底將頁面放在基礎 PDF 下方(如信頭或背景)。",
|
||||
"faqSinglePageQuestion": "我可以使用單頁 PDF 作為所有頁面的疊加嗎?",
|
||||
"faqSinglePageAnswer": "可以!啟用「循環」選項,疊加頁面將在所有基礎頁面上重複。單頁浮水印將套用到每一頁。",
|
||||
"faqPrivacyQuestion": "我的檔案是否私密安全?",
|
||||
"faqPrivacyAnswer": "當然!所有處理都在您的瀏覽器中進行。您的檔案永遠不會離開您的裝置,確保完全隱私。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,5 +773,32 @@
|
||||
"failedLoadTemplate": "加载模板失败。",
|
||||
"noSettings": "此节点没有可配置的设置。",
|
||||
"advancedSettings": "高级设置"
|
||||
},
|
||||
"pdfOverlay": {
|
||||
"name": "PDF 叠加",
|
||||
"subtitle": "将一个 PDF 的页面叠加或衬垫到另一个 PDF 上。",
|
||||
"basePdfLabel": "基础 PDF",
|
||||
"uploadBasePdf": "上传基础 PDF",
|
||||
"overlayPdfLabel": "叠加 / 衬底 PDF",
|
||||
"uploadOverlayPdf": "上传叠加/衬底 PDF",
|
||||
"modeLabel": "模式",
|
||||
"overlayOption": "叠加(在页面上方)",
|
||||
"underlayOption": "衬底(在页面下方)",
|
||||
"pageRangeLabel": "应用到页面(可选)",
|
||||
"pageRangeHint": "使用 \"z\" 表示最后一页。留空则应用到所有页面。",
|
||||
"repeatLabel": "如果基础文件更长,则循环叠加/衬底页面",
|
||||
"processButton": "应用叠加 / 衬底",
|
||||
"howItWorksUploadTitle": "上传两个 PDF",
|
||||
"howItWorksUploadDescription": "上传您的基础 PDF 和要叠加或衬底的 PDF。",
|
||||
"howItWorksModeTitle": "选择模式",
|
||||
"howItWorksModeDescription": "选择叠加(上方)或衬底(下方)。可选设置页面范围和重复。",
|
||||
"howItWorksDownloadTitle": "下载",
|
||||
"howItWorksDownloadDescription": "下载应用了叠加或衬底的 PDF。",
|
||||
"faqDifferenceQuestion": "叠加和衬底有什么区别?",
|
||||
"faqDifferenceAnswer": "叠加将页面放在基础 PDF 上方(如水印)。衬底将页面放在基础 PDF 下方(如信头或背景)。",
|
||||
"faqSinglePageQuestion": "我可以使用单页 PDF 作为所有页面的叠加吗?",
|
||||
"faqSinglePageAnswer": "可以!启用\"循环\"选项,叠加页面将在所有基础页面上重复。单页水印将应用到每一页。",
|
||||
"faqPrivacyQuestion": "我的文件是否私密安全?",
|
||||
"faqPrivacyAnswer": "当然!所有处理都在您的浏览器中进行。您的文件永远不会离开您的设备,确保完全隐私。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,6 +545,12 @@ const baseCategories = [
|
||||
icon: 'ph-files',
|
||||
subtitle: 'Duplicate, reorder, and delete pages.',
|
||||
},
|
||||
{
|
||||
href: import.meta.env.BASE_URL + 'overlay-pdf.html',
|
||||
name: 'PDF Overlay',
|
||||
icon: 'ph-stack-simple',
|
||||
subtitle: 'Overlay or underlay pages from one PDF onto another.',
|
||||
},
|
||||
{
|
||||
href: import.meta.env.BASE_URL + 'add-attachments.html',
|
||||
name: 'Add Attachments',
|
||||
|
||||
288
src/js/logic/overlay-pdf-page.ts
Normal file
288
src/js/logic/overlay-pdf-page.ts
Normal file
@@ -0,0 +1,288 @@
|
||||
import { showAlert } from '../ui.js';
|
||||
import {
|
||||
downloadFile,
|
||||
formatBytes,
|
||||
initializeQpdf,
|
||||
readFileAsArrayBuffer,
|
||||
} from '../utils/helpers.js';
|
||||
import { icons, createIcons } from 'lucide';
|
||||
import type { OverlayPdfState, QpdfInstanceExtended } from '@/types';
|
||||
|
||||
const pageState: OverlayPdfState = {
|
||||
baseFile: null,
|
||||
overlayFile: null,
|
||||
};
|
||||
|
||||
function resetState() {
|
||||
pageState.baseFile = null;
|
||||
pageState.overlayFile = null;
|
||||
|
||||
const baseDisplay = document.getElementById('base-file-display');
|
||||
if (baseDisplay) baseDisplay.innerHTML = '';
|
||||
|
||||
const overlayDisplay = document.getElementById('overlay-file-display');
|
||||
if (overlayDisplay) overlayDisplay.innerHTML = '';
|
||||
|
||||
const toolOptions = document.getElementById('tool-options');
|
||||
if (toolOptions) toolOptions.classList.add('hidden');
|
||||
|
||||
const baseInput = document.getElementById(
|
||||
'base-file-input'
|
||||
) as HTMLInputElement;
|
||||
if (baseInput) baseInput.value = '';
|
||||
|
||||
const overlayInput = document.getElementById(
|
||||
'overlay-file-input'
|
||||
) as HTMLInputElement;
|
||||
if (overlayInput) overlayInput.value = '';
|
||||
}
|
||||
|
||||
function renderFileEntry(
|
||||
container: HTMLElement,
|
||||
file: File,
|
||||
onRemove: () => void
|
||||
) {
|
||||
container.innerHTML = '';
|
||||
|
||||
const fileDiv = document.createElement('div');
|
||||
fileDiv.className =
|
||||
'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';
|
||||
|
||||
const infoContainer = document.createElement('div');
|
||||
infoContainer.className = 'flex flex-col overflow-hidden';
|
||||
|
||||
const nameSpan = document.createElement('div');
|
||||
nameSpan.className = 'truncate font-medium text-gray-200 text-sm mb-1';
|
||||
nameSpan.textContent = file.name;
|
||||
|
||||
const metaSpan = document.createElement('div');
|
||||
metaSpan.className = 'text-xs text-gray-400';
|
||||
metaSpan.textContent = formatBytes(file.size);
|
||||
|
||||
infoContainer.append(nameSpan, metaSpan);
|
||||
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.className = 'ml-4 text-red-400 hover:text-red-300 flex-shrink-0';
|
||||
removeBtn.innerHTML = '<i data-lucide="trash-2" class="w-4 h-4"></i>';
|
||||
removeBtn.onclick = onRemove;
|
||||
|
||||
fileDiv.append(infoContainer, removeBtn);
|
||||
container.appendChild(fileDiv);
|
||||
createIcons({ icons });
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
const toolOptions = document.getElementById('tool-options');
|
||||
const baseDisplay = document.getElementById('base-file-display');
|
||||
const overlayDisplay = document.getElementById('overlay-file-display');
|
||||
|
||||
if (baseDisplay && pageState.baseFile) {
|
||||
renderFileEntry(baseDisplay, pageState.baseFile, () => {
|
||||
pageState.baseFile = null;
|
||||
baseDisplay.innerHTML = '';
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
if (overlayDisplay && pageState.overlayFile) {
|
||||
renderFileEntry(overlayDisplay, pageState.overlayFile, () => {
|
||||
pageState.overlayFile = null;
|
||||
overlayDisplay.innerHTML = '';
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
if (toolOptions) {
|
||||
if (pageState.baseFile && pageState.overlayFile) {
|
||||
toolOptions.classList.remove('hidden');
|
||||
} else {
|
||||
toolOptions.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isPdf(file: File): boolean {
|
||||
return (
|
||||
file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')
|
||||
);
|
||||
}
|
||||
|
||||
async function processOverlay() {
|
||||
if (!pageState.baseFile || !pageState.overlayFile) {
|
||||
showAlert(
|
||||
'Missing Files',
|
||||
'Please upload both a base PDF and an overlay/underlay PDF.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const loaderModal = document.getElementById('loader-modal');
|
||||
const loaderText = document.getElementById('loader-text');
|
||||
if (loaderModal) loaderModal.classList.remove('hidden');
|
||||
if (loaderText) loaderText.textContent = 'Initializing PDF engine...';
|
||||
|
||||
const inputPath = '/input_base.pdf';
|
||||
const overlayPath = '/input_overlay.pdf';
|
||||
const outputPath = '/output.pdf';
|
||||
let qpdf: QpdfInstanceExtended;
|
||||
|
||||
try {
|
||||
qpdf = await initializeQpdf();
|
||||
|
||||
if (loaderText) loaderText.textContent = 'Reading files...';
|
||||
|
||||
const baseBuffer = await readFileAsArrayBuffer(pageState.baseFile);
|
||||
const overlayBuffer = await readFileAsArrayBuffer(pageState.overlayFile);
|
||||
|
||||
qpdf.FS.writeFile(inputPath, new Uint8Array(baseBuffer as ArrayBuffer));
|
||||
qpdf.FS.writeFile(
|
||||
overlayPath,
|
||||
new Uint8Array(overlayBuffer as ArrayBuffer)
|
||||
);
|
||||
|
||||
const modeSelect = document.getElementById(
|
||||
'mode-select'
|
||||
) as HTMLSelectElement;
|
||||
const pageRangeInput = document.getElementById(
|
||||
'page-range'
|
||||
) as HTMLInputElement;
|
||||
const repeatCheckbox = document.getElementById(
|
||||
'repeat-toggle'
|
||||
) as HTMLInputElement;
|
||||
|
||||
const mode = modeSelect?.value === 'underlay' ? '--underlay' : '--overlay';
|
||||
const pageRange = pageRangeInput?.value.trim();
|
||||
const shouldRepeat = repeatCheckbox?.checked;
|
||||
|
||||
if (loaderText)
|
||||
loaderText.textContent = `Applying ${mode.replace('--', '')}...`;
|
||||
|
||||
const args = [inputPath, mode, overlayPath];
|
||||
|
||||
if (pageRange) {
|
||||
args.push(`--to=${pageRange}`);
|
||||
}
|
||||
|
||||
if (shouldRepeat) {
|
||||
args.push('--from=', '--repeat=1-z');
|
||||
}
|
||||
|
||||
args.push('--', outputPath);
|
||||
|
||||
qpdf.callMain(args);
|
||||
|
||||
const outputFile = qpdf.FS.readFile(outputPath, { encoding: 'binary' });
|
||||
if (!outputFile || outputFile.length === 0) {
|
||||
throw new Error('Processing produced an empty file.');
|
||||
}
|
||||
|
||||
const modeLabel = mode.replace('--', '');
|
||||
const baseName = pageState.baseFile.name.replace(/\.pdf$/i, '');
|
||||
const fileName = `${baseName}_${modeLabel}.pdf`;
|
||||
|
||||
downloadFile(
|
||||
new Blob([new Uint8Array(outputFile)], { type: 'application/pdf' }),
|
||||
fileName
|
||||
);
|
||||
|
||||
showAlert(
|
||||
'Success',
|
||||
`PDF ${modeLabel} applied successfully.`,
|
||||
'success',
|
||||
() => {
|
||||
resetState();
|
||||
}
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.error('Overlay/underlay error:', error);
|
||||
showAlert(
|
||||
'Processing Failed',
|
||||
`An error occurred: ${error instanceof Error ? error.message : 'Unknown error'}.`
|
||||
);
|
||||
} finally {
|
||||
try {
|
||||
if (qpdf?.FS) {
|
||||
if (qpdf.FS.analyzePath(inputPath).exists) qpdf.FS.unlink(inputPath);
|
||||
if (qpdf.FS.analyzePath(overlayPath).exists)
|
||||
qpdf.FS.unlink(overlayPath);
|
||||
if (qpdf.FS.analyzePath(outputPath).exists) qpdf.FS.unlink(outputPath);
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
console.warn('Failed to cleanup WASM FS:', cleanupError);
|
||||
}
|
||||
if (loaderModal) loaderModal.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function setupDropZone(
|
||||
dropZone: HTMLElement,
|
||||
fileInput: HTMLInputElement,
|
||||
onFile: (file: File) => void
|
||||
) {
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const files = (e.target as HTMLInputElement).files;
|
||||
if (files && files.length > 0 && isPdf(files[0])) {
|
||||
onFile(files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
dropZone.classList.add('bg-gray-700');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragleave', (e) => {
|
||||
e.preventDefault();
|
||||
dropZone.classList.remove('bg-gray-700');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
dropZone.classList.remove('bg-gray-700');
|
||||
const files = e.dataTransfer?.files;
|
||||
if (files && files.length > 0 && isPdf(files[0])) {
|
||||
onFile(files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
fileInput.addEventListener('click', () => {
|
||||
fileInput.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const baseDropZone = document.getElementById('base-drop-zone');
|
||||
const baseInput = document.getElementById(
|
||||
'base-file-input'
|
||||
) as HTMLInputElement;
|
||||
const overlayDropZone = document.getElementById('overlay-drop-zone');
|
||||
const overlayInput = document.getElementById(
|
||||
'overlay-file-input'
|
||||
) as HTMLInputElement;
|
||||
const processBtn = document.getElementById('process-btn');
|
||||
const backBtn = document.getElementById('back-to-tools');
|
||||
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', () => {
|
||||
window.location.href = import.meta.env.BASE_URL;
|
||||
});
|
||||
}
|
||||
|
||||
if (baseDropZone && baseInput) {
|
||||
setupDropZone(baseDropZone, baseInput, (file) => {
|
||||
pageState.baseFile = file;
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
if (overlayDropZone && overlayInput) {
|
||||
setupDropZone(overlayDropZone, overlayInput, (file) => {
|
||||
pageState.overlayFile = file;
|
||||
updateUI();
|
||||
});
|
||||
}
|
||||
|
||||
if (processBtn) {
|
||||
processBtn.addEventListener('click', processOverlay);
|
||||
}
|
||||
});
|
||||
@@ -207,6 +207,7 @@ const init = async () => {
|
||||
'PDF to JSON': 'tools:pdfToJson',
|
||||
'OCR PDF': 'tools:ocrPdf',
|
||||
'Alternate & Mix Pages': 'tools:alternateMix',
|
||||
'PDF Overlay': 'tools:pdfOverlay',
|
||||
'Organize & Duplicate': 'tools:duplicateOrganize',
|
||||
'Add Attachments': 'tools:addAttachments',
|
||||
'Extract Attachments': 'tools:extractAttachments',
|
||||
|
||||
@@ -65,3 +65,4 @@ export * from './shortcuts-type.ts';
|
||||
export * from './ui-type.ts';
|
||||
export * from './markdown-editor-type.ts';
|
||||
export * from './sanitize-type.ts';
|
||||
export * from './overlay-pdf-type.ts';
|
||||
|
||||
4
src/js/types/overlay-pdf-type.ts
Normal file
4
src/js/types/overlay-pdf-type.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface OverlayPdfState {
|
||||
baseFile: File | null;
|
||||
overlayFile: File | null;
|
||||
}
|
||||
92
src/js/workflow/nodes/overlay-node.ts
Normal file
92
src/js/workflow/nodes/overlay-node.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { ClassicPreset } from 'rete';
|
||||
import { BaseWorkflowNode } from './base-node';
|
||||
import { pdfSocket } from '../sockets';
|
||||
import type { SocketData } from '../types';
|
||||
import { requirePdfInput, extractSinglePdf } from '../types';
|
||||
import { initializeQpdf } from '../../utils/helpers.js';
|
||||
import { loadPdfDocument } from '../../utils/load-pdf-document.js';
|
||||
|
||||
export class OverlayNode extends BaseWorkflowNode {
|
||||
readonly category = 'Organize & Manage' as const;
|
||||
readonly icon = 'ph-stack-simple';
|
||||
readonly description = 'Overlay or underlay pages from one PDF onto another';
|
||||
|
||||
constructor() {
|
||||
super('Overlay');
|
||||
this.addInput('pdf', new ClassicPreset.Input(pdfSocket, 'Base PDF'));
|
||||
this.addInput('overlay', new ClassicPreset.Input(pdfSocket, 'Overlay PDF'));
|
||||
this.addOutput('pdf', new ClassicPreset.Output(pdfSocket, 'Result PDF'));
|
||||
this.addControl(
|
||||
'mode',
|
||||
new ClassicPreset.InputControl('text', { initial: 'overlay' })
|
||||
);
|
||||
}
|
||||
|
||||
async data(
|
||||
inputs: Record<string, SocketData[]>
|
||||
): Promise<Record<string, SocketData>> {
|
||||
const baseInputs = requirePdfInput(inputs, 'Overlay');
|
||||
const overlayInputs = inputs['overlay'];
|
||||
if (!overlayInputs || overlayInputs.length === 0) {
|
||||
throw new Error('Overlay node requires an overlay PDF input.');
|
||||
}
|
||||
|
||||
const basePdf = extractSinglePdf(baseInputs[0]);
|
||||
const overlayPdf = extractSinglePdf(overlayInputs[0]);
|
||||
|
||||
const modeControl = this.controls[
|
||||
'mode'
|
||||
] as ClassicPreset.InputControl<'text'>;
|
||||
const mode = modeControl?.value === 'underlay' ? '--underlay' : '--overlay';
|
||||
|
||||
const qpdf = await initializeQpdf();
|
||||
const uid = `${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
||||
const inputPath = `/tmp/input_overlay_${uid}.pdf`;
|
||||
const overlayPath = `/tmp/overlay_${uid}.pdf`;
|
||||
const outputPath = `/tmp/output_overlay_${uid}.pdf`;
|
||||
|
||||
let resultBytes: Uint8Array;
|
||||
try {
|
||||
qpdf.FS.writeFile(inputPath, basePdf.bytes);
|
||||
qpdf.FS.writeFile(overlayPath, overlayPdf.bytes);
|
||||
qpdf.callMain([
|
||||
inputPath,
|
||||
mode,
|
||||
overlayPath,
|
||||
'--from=',
|
||||
'--repeat=1-z',
|
||||
'--',
|
||||
outputPath,
|
||||
]);
|
||||
resultBytes = new Uint8Array(qpdf.FS.readFile(outputPath));
|
||||
} finally {
|
||||
try {
|
||||
qpdf.FS.unlink(inputPath);
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
try {
|
||||
qpdf.FS.unlink(overlayPath);
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
try {
|
||||
qpdf.FS.unlink(outputPath);
|
||||
} catch {
|
||||
void 0;
|
||||
}
|
||||
}
|
||||
|
||||
const document = await loadPdfDocument(resultBytes);
|
||||
const modeLabel = mode.replace('--', '');
|
||||
|
||||
return {
|
||||
pdf: {
|
||||
type: 'pdf',
|
||||
document,
|
||||
bytes: resultBytes,
|
||||
filename: basePdf.filename.replace(/\.pdf$/i, `_${modeLabel}.pdf`),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ import { DeskewNode } from './deskew-node';
|
||||
import { PdfToPdfANode } from './pdf-to-pdfa-node';
|
||||
import { PosterizeNode } from './posterize-node';
|
||||
import { BookletNode } from './booklet-node';
|
||||
import { OverlayNode } from './overlay-node';
|
||||
import { FontToOutlineNode } from './font-to-outline-node';
|
||||
import { TableOfContentsNode } from './table-of-contents-node';
|
||||
import { EmailToPdfNode } from './email-to-pdf-node';
|
||||
@@ -355,6 +356,14 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
factory: () => new BookletNode(),
|
||||
toolPageId: 'pdf-booklet',
|
||||
},
|
||||
OverlayNode: {
|
||||
label: 'Overlay',
|
||||
category: 'Organize & Manage',
|
||||
icon: 'ph-stack-simple',
|
||||
description: 'Overlay or underlay pages from one PDF onto another',
|
||||
factory: () => new OverlayNode(),
|
||||
toolPageId: 'overlay-pdf',
|
||||
},
|
||||
PosterizeNode: {
|
||||
label: 'Posterize',
|
||||
category: 'Organize & Manage',
|
||||
|
||||
518
src/pages/overlay-pdf.html
Normal file
518
src/pages/overlay-pdf.html
Normal file
@@ -0,0 +1,518 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>PDF Overlay & Underlay Online Free | BentoPDF</title>
|
||||
<meta
|
||||
name="title"
|
||||
content="PDF Overlay & Underlay Online Free | BentoPDF"
|
||||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="★ Overlay or underlay PDF pages online free ★ Add watermarks, letterheads, backgrounds ★ No signup ★ Privacy-first ★ Works in browser"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="pdf overlay, pdf underlay, pdf watermark, pdf background, overlay pages"
|
||||
/>
|
||||
<meta name="author" content="BentoPDF" />
|
||||
<meta
|
||||
name="robots"
|
||||
content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"
|
||||
/>
|
||||
|
||||
<link rel="canonical" href="https://www.bentopdf.com/overlay-pdf.html" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://www.bentopdf.com/overlay-pdf" />
|
||||
<meta
|
||||
property="og:title"
|
||||
content="PDF Overlay & Underlay Online Free | BentoPDF"
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="★ Overlay or underlay PDF pages online free ★ Add watermarks, letterheads, backgrounds ★ No signup ★ Privacy-first ★ Works in browser"
|
||||
/>
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://www.bentopdf.com/images/og-overlay-pdf.png"
|
||||
/>
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:site_name" content="BentoPDF" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:url" content="https://www.bentopdf.com/overlay-pdf" />
|
||||
<meta name="twitter:title" content="PDF Overlay & Underlay Free" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="★ Overlay or underlay PDF pages online free ★ No signup ★ Privacy-first ★ Works in browser"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://www.bentopdf.com/images/twitter-overlay-pdf.png"
|
||||
/>
|
||||
<meta name="twitter:site" content="@BentoPDF" />
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="PDF Overlay" />
|
||||
|
||||
<link href="/src/css/styles.css" rel="stylesheet" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="icon" type="image/svg+xml" href="/images/favicon.svg" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/images/favicon-192x192.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="512x512"
|
||||
href="/images/favicon-512x512.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" sizes="32x32" />
|
||||
</head>
|
||||
|
||||
<body class="antialiased bg-gray-900">
|
||||
{{> navbar }}
|
||||
|
||||
<div
|
||||
id="uploader"
|
||||
class="min-h-screen flex flex-col items-center justify-start py-12 p-4 bg-gray-900"
|
||||
>
|
||||
<div
|
||||
id="tool-uploader"
|
||||
class="bg-gray-800 rounded-xl shadow-xl px-4 py-8 md:p-8 max-w-2xl w-full text-gray-200 border border-gray-700"
|
||||
>
|
||||
<button
|
||||
id="back-to-tools"
|
||||
class="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 mb-6 font-semibold"
|
||||
>
|
||||
<i data-lucide="arrow-left" class="cursor-pointer"></i>
|
||||
<span class="cursor-pointer" data-i18n="tools.backToTools">
|
||||
Back to Tools
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<h1
|
||||
class="text-2xl font-bold text-white mb-2"
|
||||
data-i18n="tools:pdfOverlay.name"
|
||||
>
|
||||
PDF Overlay
|
||||
</h1>
|
||||
<p class="text-gray-400 mb-6" data-i18n="tools:pdfOverlay.subtitle">
|
||||
Overlay or underlay pages from one PDF onto another.
|
||||
</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-300 mb-2"
|
||||
data-i18n="tools:pdfOverlay.basePdfLabel"
|
||||
>Base PDF</label
|
||||
>
|
||||
<div
|
||||
id="base-drop-zone"
|
||||
class="relative flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-900 hover:bg-gray-700 transition-colors duration-300"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center py-4">
|
||||
<i
|
||||
data-lucide="file-text"
|
||||
class="w-8 h-8 mb-2 text-gray-400"
|
||||
></i>
|
||||
<p class="text-sm text-gray-400">
|
||||
<span
|
||||
class="font-semibold"
|
||||
data-i18n="tools:pdfOverlay.uploadBasePdf"
|
||||
>Upload base PDF</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
id="base-file-input"
|
||||
type="file"
|
||||
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</div>
|
||||
<div id="base-file-display" class="mt-2"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
class="block text-sm font-medium text-gray-300 mb-2"
|
||||
data-i18n="tools:pdfOverlay.overlayPdfLabel"
|
||||
>Overlay / Underlay PDF</label
|
||||
>
|
||||
<div
|
||||
id="overlay-drop-zone"
|
||||
class="relative flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-900 hover:bg-gray-700 transition-colors duration-300"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center py-4">
|
||||
<i data-lucide="layers" class="w-8 h-8 mb-2 text-gray-400"></i>
|
||||
<p class="text-sm text-gray-400">
|
||||
<span
|
||||
class="font-semibold"
|
||||
data-i18n="tools:pdfOverlay.uploadOverlayPdf"
|
||||
>Upload overlay/underlay PDF</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
id="overlay-file-input"
|
||||
type="file"
|
||||
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
|
||||
accept="application/pdf"
|
||||
/>
|
||||
</div>
|
||||
<div id="overlay-file-display" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="text-xs text-gray-500 mt-3"
|
||||
data-i18n="upload.filesNeverLeave"
|
||||
>
|
||||
Your files never leave your device.
|
||||
</p>
|
||||
|
||||
<div id="tool-options" class="hidden mt-6 space-y-4">
|
||||
<div
|
||||
class="p-4 bg-gray-900 rounded-lg border border-gray-700 space-y-4"
|
||||
>
|
||||
<div>
|
||||
<label
|
||||
for="mode-select"
|
||||
class="block text-sm font-medium text-gray-300 mb-1"
|
||||
data-i18n="tools:pdfOverlay.modeLabel"
|
||||
>Mode</label
|
||||
>
|
||||
<select
|
||||
id="mode-select"
|
||||
class="w-full bg-gray-800 border border-gray-600 text-white rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
>
|
||||
<option
|
||||
value="overlay"
|
||||
data-i18n="tools:pdfOverlay.overlayOption"
|
||||
>
|
||||
Overlay (on top of pages)
|
||||
</option>
|
||||
<option
|
||||
value="underlay"
|
||||
data-i18n="tools:pdfOverlay.underlayOption"
|
||||
>
|
||||
Underlay (behind pages)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
for="page-range"
|
||||
class="block text-sm font-medium text-gray-300 mb-1"
|
||||
data-i18n="tools:pdfOverlay.pageRangeLabel"
|
||||
>Apply to pages (optional)</label
|
||||
>
|
||||
<input
|
||||
id="page-range"
|
||||
type="text"
|
||||
placeholder="e.g. 1-5, 8, 10-z (leave empty for all)"
|
||||
class="w-full bg-gray-800 border border-gray-600 text-white rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
||||
/>
|
||||
<p
|
||||
class="text-xs text-gray-500 mt-1"
|
||||
data-i18n="tools:pdfOverlay.pageRangeHint"
|
||||
>
|
||||
Use "z" for the last page. Leave empty to apply to all pages.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
id="repeat-toggle"
|
||||
type="checkbox"
|
||||
checked
|
||||
class="w-4 h-4 rounded border-gray-600 bg-gray-800 text-indigo-600 focus:ring-indigo-500"
|
||||
/>
|
||||
<label
|
||||
for="repeat-toggle"
|
||||
class="text-sm text-gray-300"
|
||||
data-i18n="tools:pdfOverlay.repeatLabel"
|
||||
>Loop overlay/underlay pages if base is longer</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="process-btn"
|
||||
class="btn-gradient w-full"
|
||||
data-i18n="tools:pdfOverlay.processButton"
|
||||
>
|
||||
Apply Overlay / Underlay
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="loader-modal"
|
||||
class="hidden fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50"
|
||||
>
|
||||
<div
|
||||
class="bg-gray-800 p-8 rounded-lg flex flex-col items-center gap-4 border border-gray-700 shadow-xl"
|
||||
>
|
||||
<div class="solid-spinner"></div>
|
||||
<p
|
||||
id="loader-text"
|
||||
class="text-white text-lg font-medium"
|
||||
data-i18n="loader.processing"
|
||||
>
|
||||
Processing...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="alert-modal"
|
||||
class="fixed inset-0 bg-gray-900 bg-opacity-90 flex items-center justify-center z-50 hidden"
|
||||
>
|
||||
<div
|
||||
class="bg-gray-800 rounded-lg shadow-xl p-6 max-w-sm w-full border border-gray-700"
|
||||
>
|
||||
<h3 id="alert-title" class="text-xl font-bold text-white mb-2">
|
||||
Alert
|
||||
</h3>
|
||||
<p id="alert-message" class="text-gray-300 mb-6"></p>
|
||||
<button
|
||||
id="alert-ok"
|
||||
class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="max-w-4xl mx-auto px-4 py-12">
|
||||
<h2
|
||||
class="text-2xl md:text-3xl font-bold text-white mb-8 text-center"
|
||||
data-i18n="howItWorks.title"
|
||||
>
|
||||
How It Works
|
||||
</h2>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="flex-shrink-0 w-10 h-10 bg-indigo-600 rounded-full flex items-center justify-center text-white font-bold"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3
|
||||
class="text-lg font-semibold text-white mb-1"
|
||||
data-i18n="tools:pdfOverlay.howItWorksUploadTitle"
|
||||
>
|
||||
Upload Two PDFs
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.howItWorksUploadDescription"
|
||||
>
|
||||
Upload your base PDF and the PDF you want to overlay or underlay.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="flex-shrink-0 w-10 h-10 bg-indigo-600 rounded-full flex items-center justify-center text-white font-bold"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3
|
||||
class="text-lg font-semibold text-white mb-1"
|
||||
data-i18n="tools:pdfOverlay.howItWorksModeTitle"
|
||||
>
|
||||
Choose Mode
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.howItWorksModeDescription"
|
||||
>
|
||||
Select overlay (on top) or underlay (behind). Optionally set a
|
||||
page range and repeat.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="flex-shrink-0 w-10 h-10 bg-indigo-600 rounded-full flex items-center justify-center text-white font-bold"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3
|
||||
class="text-lg font-semibold text-white mb-1"
|
||||
data-i18n="tools:pdfOverlay.howItWorksDownloadTitle"
|
||||
>
|
||||
Download
|
||||
</h3>
|
||||
<p
|
||||
class="text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.howItWorksDownloadDescription"
|
||||
>
|
||||
Download your PDF with the overlay or underlay applied.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="max-w-4xl mx-auto px-4 py-12">
|
||||
<h2
|
||||
class="text-2xl md:text-3xl font-bold text-white mb-6 text-center"
|
||||
data-i18n="faq.sectionTitle"
|
||||
>
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<details class="bg-gray-800 p-5 rounded-lg border border-gray-700">
|
||||
<summary
|
||||
class="cursor-pointer font-semibold text-white flex items-center justify-between"
|
||||
>
|
||||
<span data-i18n="tools:pdfOverlay.faqDifferenceQuestion"
|
||||
>What is the difference between overlay and underlay?</span
|
||||
>
|
||||
<i data-lucide="chevron-down" class="w-5 h-5"></i>
|
||||
</summary>
|
||||
<p
|
||||
class="mt-3 text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.faqDifferenceAnswer"
|
||||
>
|
||||
Overlay places pages on top of your base PDF (like a watermark).
|
||||
Underlay places pages behind your base PDF (like a letterhead or
|
||||
background).
|
||||
</p>
|
||||
</details>
|
||||
<details class="bg-gray-800 p-5 rounded-lg border border-gray-700">
|
||||
<summary
|
||||
class="cursor-pointer font-semibold text-white flex items-center justify-between"
|
||||
>
|
||||
<span data-i18n="tools:pdfOverlay.faqSinglePageQuestion"
|
||||
>Can I use a single-page PDF as overlay for all pages?</span
|
||||
>
|
||||
<i data-lucide="chevron-down" class="w-5 h-5"></i>
|
||||
</summary>
|
||||
<p
|
||||
class="mt-3 text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.faqSinglePageAnswer"
|
||||
>
|
||||
Yes! Enable the "Loop" option and your overlay PDF pages will repeat
|
||||
across all base pages. A single-page watermark will apply to every
|
||||
page.
|
||||
</p>
|
||||
</details>
|
||||
<details class="bg-gray-800 p-5 rounded-lg border border-gray-700">
|
||||
<summary
|
||||
class="cursor-pointer font-semibold text-white flex items-center justify-between"
|
||||
>
|
||||
<span data-i18n="tools:pdfOverlay.faqPrivacyQuestion"
|
||||
>Are my files private and secure?</span
|
||||
>
|
||||
<i data-lucide="chevron-down" class="w-5 h-5"></i>
|
||||
</summary>
|
||||
<p
|
||||
class="mt-3 text-gray-400"
|
||||
data-i18n="tools:pdfOverlay.faqPrivacyAnswer"
|
||||
>
|
||||
Absolutely! All processing happens in your browser. Your files never
|
||||
leave your device, ensuring complete privacy.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{> footer }}
|
||||
<script type="module" src="/src/js/utils/lucide-init.ts"></script>
|
||||
<script type="module" src="/src/js/utils/full-width.ts"></script>
|
||||
<script type="module" src="/src/js/utils/simple-mode-footer.ts"></script>
|
||||
<script type="module" src="/src/version.ts"></script>
|
||||
<script type="module" src="/src/js/logic/overlay-pdf-page.ts"></script>
|
||||
<script type="module" src="/src/js/mobileMenu.ts"></script>
|
||||
<script type="module" src="/src/js/main.ts"></script>
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "PDF Overlay - BentoPDF",
|
||||
"applicationCategory": "PDF Tool",
|
||||
"operatingSystem": "Any - Web Browser",
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "USD"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "HowTo",
|
||||
"name": "How to overlay or underlay PDF pages",
|
||||
"description": "Learn how to overlay or underlay pages from one PDF onto another using BentoPDF",
|
||||
"step": [
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"position": 1,
|
||||
"name": "Upload Two PDFs",
|
||||
"text": "Upload your base PDF and the overlay/underlay PDF"
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"position": 2,
|
||||
"name": "Choose Mode",
|
||||
"text": "Select overlay or underlay mode and configure options"
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"position": 3,
|
||||
"name": "Download",
|
||||
"text": "Download your processed PDF"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 1,
|
||||
"name": "Home",
|
||||
"item": "https://www.bentopdf.com"
|
||||
},
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": 2,
|
||||
"name": "PDF Overlay",
|
||||
"item": "https://www.bentopdf.com/overlay-pdf"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -494,6 +494,7 @@ export default defineConfig(() => {
|
||||
'extract-pages': resolve(__dirname, 'src/pages/extract-pages.html'),
|
||||
'delete-pages': resolve(__dirname, 'src/pages/delete-pages.html'),
|
||||
'organize-pdf': resolve(__dirname, 'src/pages/organize-pdf.html'),
|
||||
'overlay-pdf': resolve(__dirname, 'src/pages/overlay-pdf.html'),
|
||||
'page-numbers': resolve(__dirname, 'src/pages/page-numbers.html'),
|
||||
'add-page-labels': resolve(
|
||||
__dirname,
|
||||
|
||||
Reference in New Issue
Block a user