Compare commits

...

43 Commits

Author SHA1 Message Date
Anthony Stirling
c613164003 Update build.gradle 2023-02-24 22:49:02 +00:00
Anthony Stirling
67345d083e error handling, excel, bug fixes, name fixes (#57) 2023-02-24 22:47:26 +00:00
Anthony Stirling
6135d08154 Update remove-password.html 2023-02-20 19:00:00 +00:00
Anthony Stirling
340cbb9acf Update README.md 2023-02-12 18:24:13 +00:00
Anthony Stirling
57b67fdeb1 badges (#48)
* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md
2023-02-12 15:55:47 +00:00
Anthony Stirling
b40d324e04 nullcheck fixes 2023-02-11 15:17:17 +00:00
Anthony Stirling
54e1ced26a Update build.gradle 2023-02-11 14:28:06 +00:00
Anthony Stirling
c739c9dd2b Metadata editting and local only JS and pdf to image change and format pages (#44)
* Formatting

* changeMeta

* pdf to img fix

* foramtting

* new image

* lang changes
2023-02-11 14:27:15 +00:00
Anthony Stirling
aa9f8329d5 Merge pull request #43 from Sf298/localisationAndIcons
Manually added combined changes that wouldn't merge
2023-02-07 20:19:18 +00:00
Saud Fatayerji
9b3aac4a8a Combined changes that wouldn't merge 2023-02-07 23:14:03 +03:00
Saud Fatayerji
75d841083c Fixed formatting 2023-02-07 15:56:50 +00:00
Saud Fatayerji
a01ad71414 Added french and arabic to dropdown 2023-02-07 15:56:50 +00:00
Saud Fatayerji
9e6771d0f8 Fixed alignment for rtl languages. other alignment fixes 2023-02-07 15:56:50 +00:00
Saud Fatayerji
c83c57a37e Fixed file encoding for messages_xx.properties files 2023-02-07 15:56:50 +00:00
Saud Fatayerji
90ef6deca8 Dynamic set the html lang attribute 2023-02-07 15:56:50 +00:00
Saud Fatayerji
83bd2d5915 Added Arabic 2023-02-07 15:56:50 +00:00
Saud Fatayerji
879f558b44 Added French 2023-02-07 15:56:50 +00:00
Anthony Stirling
f7d320ac32 Update push-docker.yml 2023-02-06 12:39:26 +00:00
Anthony Stirling
e40e47df7d Update push-docker.yml 2023-02-06 11:02:46 +00:00
Anthony Stirling
2146e5c8a8 Update push-docker.yml 2023-02-06 11:01:23 +00:00
Anthony Stirling
c9cb1959b3 Update WatermarkController.java 2023-02-06 10:52:11 +00:00
louiskram
cab3fe1bf4 German translation (#37)
* added german language to navbar

* added german translation file

* translated missing fragments

---------

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
2023-02-06 08:05:51 +00:00
Anthony Stirling
53057f4e18 Update push-docker.yml (#38) 2023-02-05 23:35:49 +00:00
Anthony Stirling
57b12ffcc3 Update HowToAddNewLanguage.md 2023-02-05 21:40:25 +00:00
Anthony Stirling
4c88c1bc54 Merge pull request #34 from Sf298/addGraphicsToRotatePage
Add graphics to rotate page
2023-02-05 21:28:57 +00:00
Anthony Stirling
a7b8e50159 Merge branch 'main' into addGraphicsToRotatePage 2023-02-05 21:26:50 +00:00
Saud Fatayerji
2c8e86297f Fixed missed localisation field 2023-02-06 00:20:25 +03:00
Gianni C
6a0c5914e9 Improve readability and Google indexing (#36)
* update README.md

* Update README.md

fixed for github compatible HTML
2023-02-05 21:12:03 +00:00
Saud Fatayerji
38925ea4ca Fixed merge conflict. Cleaned up code 2023-02-05 22:04:37 +03:00
Saud Fatayerji
b123e8eba5 Merge remote-tracking branch 'upstream/main' into addGraphicsToRotatePage
# Conflicts:
#	src/main/resources/templates/rotate-pdf.html
2023-02-05 18:20:41 +03:00
Saud Fatayerji
f093d65729 Fixed bugs 2023-02-05 18:08:43 +03:00
Anthony Stirling
4a95e001bb Update HowToAddNewLanguage.md 2023-02-05 12:57:48 +00:00
Anthony Stirling
6662115d10 Lang setup (#35) 2023-02-05 12:54:48 +00:00
Saud Fatayerji
58423bfc21 Redesigned the rotate page 2023-02-05 02:45:33 +03:00
Saud Fatayerji
fe3088acfc Merge pull request #1 from Frooodle/main
Update repo
2023-02-04 16:26:21 +00:00
Anthony Stirling
dd11cfab40 Fixes and logo (#33) 2023-02-04 15:06:27 +00:00
Anthony Stirling
effbb3a047 Merge pull request #31 from Sf298/cleanedUpHomePage
Cleaned up home page
2023-02-04 15:03:53 +00:00
Saud Fatayerji
c667c77582 Fixed alignment of the feature card 'go' button 2023-02-04 17:53:19 +03:00
Saud Fatayerji
ae50611acc Fixed issue where the last feature expands to fill the whole last row 2023-02-04 17:36:20 +03:00
Saud Fatayerji
f6feb7a7e7 Made pages mobile friendly 2023-02-04 16:19:11 +03:00
Saud Fatayerji
0a67da2c76 Cleaned up the code used to list features on the home page. 2023-02-04 16:15:03 +03:00
Anthony Stirling
defe6ef1e5 Merge pull request #28 from mms-gianni/main
Add Logo
2023-02-04 00:44:04 +00:00
Gianni Carafa
f00df02b21 add docs 2023-02-04 00:39:31 +01:00
76 changed files with 14450 additions and 1517 deletions

View File

@@ -7,9 +7,8 @@
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
name: "Build repo"
on:
push:
@@ -42,15 +41,15 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: java
# - name: Initialize CodeQL
# uses: github/codeql-action/init@v2
# with:
# languages: java
- uses: gradle/gradle-build-action@v2.3.3
with:
gradle-version: 7.6
arguments: assemble --no-build-cache
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v2
#- name: Perform CodeQL analysis
# uses: github/codeql-action/analyze@v2

View File

@@ -4,6 +4,7 @@ on:
push:
branches:
- master
- testGit
- main
jobs:
push:
@@ -37,20 +38,41 @@ jobs:
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_API }}
# - name: Check if tag exists
# id: checkIdExists
# continue-on-error: true
# run: |
# response=$(curl -s https://hub.docker.com/v2/repositories/frooodle/s-pdf/tags/?name=${{ steps.versionNumber.outputs.versionNumber }})
# result=$(echo $response | jq ".results")
# if [ "$result" == "[]" ]; then
# echo "Tag ${{ steps.versionNumber.outputs.versionNumber }} doesnt exist. Continuing with build and push."
# else
# echo "Tag ${{ steps.versionNumber.outputs.versionNumber }} already exists. Skipping build and push."
# exit 1;
# fi
- name: Setup buildx
run: |
docker buildx create --name mybuilder
docker buildx use mybuilder
- name: Build and push versioned amd64 and v8
if: github.ref == 'refs/heads/main'
run: |
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:${{ steps.versionNumber.outputs.versionNumber }}-alpha" .
- name: Build and push versioned amd64 and v8
if: github.ref == 'refs/heads/main'
- name: Build and push versioned amd64 and v8
if: github.ref == 'refs/heads/master'
run: |
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:${{ steps.versionNumber.outputs.versionNumber }}" .
- name: Build and push latest amd64 and v8
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master'
run: |
docker buildx build --platform="linux/amd64,linux/arm64/v8" --push --tag "frooodle/s-pdf:latest" .

5
.gitignore vendored
View File

@@ -12,7 +12,8 @@ local.properties
.settings/
.loadpath
.recommenders
.classpath
.project
# Gradle
.gradle
@@ -107,3 +108,5 @@ local.properties
*.zip
*.tar.gz
*.rar
/build

31
HowToAddNewLanguage.md Normal file
View File

@@ -0,0 +1,31 @@
<p align="center"><img src="https://raw.githubusercontent.com/Frooodle/Stirling-PDF/main/docs/stirling.png" width="80" ><br><h1 align="center">Stirling-PDF</h1>
</p>
# How to add new languages to Stirling-PDF
Fork Stirling-PDF and make a new branch out of Main
Then add reference to the language in the navbar by adding a new language entry to the dropdown
https://github.com/Frooodle/Stirling-PDF/blob/main/src/main/resources/templates/fragments/navbar.html#L80
For example to add Polish you would add
```
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">Polish</a>
```
The data-language-code is the code used to reference the file in the next step.
Start by copying the existing english property file
[https://github.com/Frooodle/Stirling-PDF/tree/langSetup/src/main/resources/messages_en_GB.properties](https://github.com/Frooodle/Stirling-PDF/blob/main/src/main/resources/messages_en_US.properties)
Copy and rename it to messages_{your data-language-code here}.properties, in the polish example you would set the name to messages_pl_PL.properties
Then simply translate all property entries within that file and make a PR into main for others to use!
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but wont be able to verify the translations themselves)

View File

@@ -1,4 +1,11 @@
# Stirling-PDF
<p align="center"><img src="https://raw.githubusercontent.com/Frooodle/Stirling-PDF/main/docs/stirling.png" width="80" ><br><h1 align="center">Stirling-PDF</h1>
</p>
[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf)
[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/Cn8pWhQRxZ)
[![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Frooodle/Stirling-PDF/)
[![GitHub Repo stars](https://img.shields.io/github/stars/frooodle/stirling-pdf?style=social)](https://github.com/Frooodle/stirling-pdf)
[![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/paypalme/froodleplex)
This is a locally hosted web application that allows you to perform various operations on PDF files, such as splitting and adding images.
@@ -16,12 +23,13 @@ I will support and fix/add things to this if there is a demand [Discord](https:/
- Merge multiple PDFs together into a single resultant file
- Convert PDFs to and from images
- Reorganize PDF pages into different orders.
- Add images to PDFs at specified locations.
- Add images to PDFs at specified locations. (WIP)
- Rotating PDFs in 90 degree increments.
- Compressing PDFs to decrease their filesize.
- Add and remove passwords
- Set PDF Permissions
- Add watermark(s)
- Edit metadata
- Dark mode support.
## Technologies used
@@ -33,7 +41,7 @@ I will support and fix/add things to this if there is a demand [Discord](https:/
## How to use
### Locally
### Locally
Prerequisites
- Java 17 or later

View File

@@ -5,7 +5,7 @@ plugins {
}
group = 'stirling.software'
version = '0.3.0'
version = '0.3.4'
sourceCompatibility = '17'
repositories {
@@ -23,6 +23,12 @@ dependencies {
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
implementation 'e-iceblue:spire.pdf.free:5.1.0'
implementation 'org.apache.poi:poi:5.2.0'
implementation 'org.apache.poi:poi-ooxml:5.2.0'
implementation 'com.itextpdf:itextpdf:5.5.13.2'
}
jar {

BIN
docs/stirling-pdf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,298 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="99.537987mm"
height="95.209366mm"
viewBox="0 0 99.537987 95.209366"
version="1.1"
id="svg745"
xml:space="preserve"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="stirling-transparent.svg"
inkscape:export-filename="stirling.png"
inkscape:export-xdpi="80"
inkscape:export-ydpi="80"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview747"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.914906"
inkscape:cx="175.42786"
inkscape:cy="510.43495"
inkscape:window-width="2256"
inkscape:window-height="1428"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
inkscape:current-layer="svg745" /><defs
id="defs742"><linearGradient
inkscape:collect="always"
id="linearGradient72198"><stop
style="stop-color:#ccd6d7;stop-opacity:1;"
offset="0"
id="stop72194" /><stop
style="stop-color:#0f3a3f;stop-opacity:1;"
offset="1"
id="stop72196" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71928"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71924" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71926" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71920"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71916" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71918" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient69598"><stop
style="stop-color:#6a0007;stop-opacity:1;"
offset="0"
id="stop69594" /><stop
style="stop-color:#b8000f;stop-opacity:1;"
offset="1"
id="stop69596" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient46361"><stop
style="stop-color:#f7f6f8;stop-opacity:1;"
offset="0"
id="stop46359" /><stop
style="stop-color:#b7b7b5;stop-opacity:1;"
offset="1"
id="stop46357" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient40554"><stop
style="stop-color:#f4f2f4;stop-opacity:1;"
offset="0"
id="stop40550" /><stop
style="stop-color:#57767b;stop-opacity:1;"
offset="1"
id="stop40552" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient39095"><stop
style="stop-color:#285459;stop-opacity:1;"
offset="0"
id="stop39093" /><stop
style="stop-color:#a6b6af;stop-opacity:1;"
offset="1"
id="stop39091" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient36672"><stop
style="stop-color:#da453f;stop-opacity:1;"
offset="0"
id="stop36668" /><stop
style="stop-color:#a60008;stop-opacity:1;"
offset="1"
id="stop36670" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient19524"><stop
style="stop-color:#3a4b4f;stop-opacity:1;"
offset="0"
id="stop19522" /><stop
style="stop-color:#617979;stop-opacity:0.97461927;"
offset="1"
id="stop19520" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient17996"><stop
style="stop-color:#401016;stop-opacity:1;"
offset="0"
id="stop17994" /><stop
style="stop-color:#761f19;stop-opacity:1;"
offset="1"
id="stop17992" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15569"><stop
style="stop-color:#8ea8ad;stop-opacity:1;"
offset="0"
id="stop15565" /><stop
style="stop-color:#e9e7eb;stop-opacity:1;"
offset="1"
id="stop15567" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15557"><stop
style="stop-color:#9b0e11;stop-opacity:1;"
offset="0"
id="stop15553" /><stop
style="stop-color:#370707;stop-opacity:1;"
offset="1"
id="stop15555" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15557"
id="linearGradient15559"
x1="120.06672"
y1="63.25761"
x2="135.16347"
y2="78.078682"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15569"
id="linearGradient15571"
x1="127.97037"
y1="101.66144"
x2="133.88971"
y2="104.77026"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient17996"
id="linearGradient17998"
x1="117.9284"
y1="86.055084"
x2="130.67392"
y2="76.945541"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient19524"
id="linearGradient19528"
x1="130.98172"
y1="82.402977"
x2="135.72115"
y2="86.45166"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient36672"
id="linearGradient36674"
x1="63.797714"
y1="74.81752"
x2="96.636673"
y2="120.29293"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient39095"
id="linearGradient39097"
x1="120.54506"
y1="124.76902"
x2="128.04152"
y2="126.0704"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient40554"
id="linearGradient40556"
x1="113.84585"
y1="114.04285"
x2="119.65858"
y2="128.50244"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient46361"
id="linearGradient46363"
x1="73.993439"
y1="114.13906"
x2="94.845322"
y2="71.247383"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient69598"
id="linearGradient69600"
x1="95.854446"
y1="114.66749"
x2="103.77842"
y2="120.1887"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71920"
id="linearGradient71922"
x1="98.580376"
y1="87.186958"
x2="118.09738"
y2="101.19449"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71928"
id="linearGradient71930"
x1="78.278267"
y1="97.433273"
x2="92.313202"
y2="104.33479"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient72198"
id="linearGradient72200"
x1="125.76636"
y1="138.46817"
x2="123.3327"
y2="126.03291"
gradientUnits="userSpaceOnUse" /></defs><g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="shadow"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient72200);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.146049,134.73858 c 0,0 11.721038,2.48294 17.938661,2.91673 6.21763,0.43378 14.75251,0.59994 22.41237,-0.43379 8.01008,-1.081 13.19907,-2.22733 14.50043,-2.66112 1.30136,-0.43379 4.00784,-1.24297 4.15244,-2.25514 0.1446,-1.01217 -3.4703,-2.74733 -6.21763,-3.32571 -2.74732,-0.57838 -12.72444,-1.44596 -14.89337,-1.44596 -2.16894,0 -37.892901,7.20499 -37.892901,7.20499 z"
id="path72192"
sodipodi:nodetypes="cssssssc" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Origami"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient40556);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.460552,134.86721 35.165798,-6.85679 16.15467,-42.7383 z"
id="path960"
sodipodi:nodetypes="cccc" /><path
style="fill:url(#linearGradient15571);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.71493,85.428056 0.3984,45.049024 -9.66213,-20.46173 z"
id="path964"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient39097);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.60518,128.00293 16.5337,2.48693 -9.68769,-20.5512 z"
id="path966"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient15559);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 118.42209,57.022622 12.70423,-2.766809 -0.0785,25.087175 -12.43878,-13.376518 z"
id="path968"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient19528);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.72114,85.386768 -4.84219,-6.459493 0.0305,11.126604 z"
id="path970"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient17998);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.10273,65.682415 11.96883,13.44935 -0.0899,10.819868 -11.88577,11.430427 z"
id="path972"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient36674);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 62.145635,130.15618 62.043392,65.435258 c 0,0 0.179321,-2.778132 1.89516,-4.036097 1.874923,-1.374597 4.341768,-1.894096 4.341768,-1.894096 l 50.91788,-11.349167 -0.0113,53.144272 -34.733274,33.56547 z"
id="path958"
sodipodi:nodetypes="ccsccccc" /></g><g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Letter"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient69600);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 94.780881,91.406803 16.870379,17.074877 -23.723345,23.00249 -18.122131,-17.99816 5.497473,-2.80607 18.404054,-0.0511 2.35163,-8.23071 z"
id="path54894"
sodipodi:nodetypes="cccccccc" /><path
style="display:inline;fill:url(#linearGradient71930);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 72.440405,92.224764 16.15467,15.745686 4.089788,-6.79927 z"
id="path54892" /><path
style="display:inline;fill:url(#linearGradient71922);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 1.124691,-14.109776 22.92453,22.286787 0.008,8.164604 -3.28863,3.16649 z"
id="path54890"
sodipodi:nodetypes="cccccc"
inkscape:label="path54890" /><path
style="display:inline;fill:url(#linearGradient46363);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 h 1.226936 l -0.05112,-14.109776 c 0,0 -5.776827,-3.220709 -12.167126,-2.40275 -6.390296,0.817957 -8.151582,2.1248 -10.58233,4.396523 -1.90229,1.777838 -2.913974,3.527446 -3.987546,7.157132 -0.512646,1.733226 -0.281963,5.988892 0.613471,8.537436 0.664591,1.891528 2.453873,4.294281 4.958868,6.134686 2.662335,1.956002 8.281825,3.527443 8.281825,3.527443 0,0 5.134614,1.887351 5.572338,4.294281 0.308137,1.69437 -0.102243,3.22071 -1.635914,4.95887 -1.258314,1.42609 -3.62969,1.99377 -6.288054,1.07357 -2.658364,-0.92021 -6.139514,-3.85065 -7.106009,-4.90775 -0.817958,-0.89464 -2.820665,-3.06173 -2.890231,-4.294021 -0.01209,-0.214205 -1.229505,-0.0963 -1.229505,-0.0963 l -0.0723,14.256941 5.879073,2.24938 c 0,0 5.214483,1.78929 8.946415,1.43143 3.731934,-0.35786 7.617235,-0.51122 11.604778,-5.16336 3.987542,-4.65213 3.680812,-12.831715 2.147141,-15.899056 -1.533673,-3.067344 -3.561212,-6.138812 -10.480087,-8.281826 -5.776829,-1.789283 -7.872846,-3.01622 -8.128458,-4.396524 -0.255611,-1.380305 0.0091,-4.253646 2.760607,-5.214481 3.220711,-1.124693 5.623462,-0.05112 7.05489,1.12469 1.431425,1.175817 5.572339,5.623462 5.572339,5.623462 z"
id="path46355"
sodipodi:nodetypes="cccssssscssssscccssssssscc" /></g></svg>

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/stirling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

310
docs/stirling.svg Normal file
View File

@@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="99.537987mm"
height="95.209366mm"
viewBox="0 0 99.537987 95.209366"
version="1.1"
id="svg745"
xml:space="preserve"
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
sodipodi:docname="stirling.svg"
inkscape:export-filename="stirling.png"
inkscape:export-xdpi="80"
inkscape:export-ydpi="80"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview747"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.914906"
inkscape:cx="184.17193"
inkscape:cy="509.88845"
inkscape:window-width="2293"
inkscape:window-height="1387"
inkscape:window-x="122"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg745" /><defs
id="defs742"><linearGradient
inkscape:collect="always"
id="linearGradient72198"><stop
style="stop-color:#ccd6d7;stop-opacity:1;"
offset="0"
id="stop72194" /><stop
style="stop-color:#0f3a3f;stop-opacity:1;"
offset="1"
id="stop72196" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71928"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71924" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71926" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71920"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71916" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71918" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient69598"><stop
style="stop-color:#6a0007;stop-opacity:1;"
offset="0"
id="stop69594" /><stop
style="stop-color:#b8000f;stop-opacity:1;"
offset="1"
id="stop69596" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient46361"><stop
style="stop-color:#f7f6f8;stop-opacity:1;"
offset="0"
id="stop46359" /><stop
style="stop-color:#b7b7b5;stop-opacity:1;"
offset="1"
id="stop46357" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient40554"><stop
style="stop-color:#f4f2f4;stop-opacity:1;"
offset="0"
id="stop40550" /><stop
style="stop-color:#57767b;stop-opacity:1;"
offset="1"
id="stop40552" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient39095"><stop
style="stop-color:#285459;stop-opacity:1;"
offset="0"
id="stop39093" /><stop
style="stop-color:#a6b6af;stop-opacity:1;"
offset="1"
id="stop39091" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient36672"><stop
style="stop-color:#da453f;stop-opacity:1;"
offset="0"
id="stop36668" /><stop
style="stop-color:#a60008;stop-opacity:1;"
offset="1"
id="stop36670" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient19524"><stop
style="stop-color:#3a4b4f;stop-opacity:1;"
offset="0"
id="stop19522" /><stop
style="stop-color:#617979;stop-opacity:0.97461927;"
offset="1"
id="stop19520" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient17996"><stop
style="stop-color:#401016;stop-opacity:1;"
offset="0"
id="stop17994" /><stop
style="stop-color:#761f19;stop-opacity:1;"
offset="1"
id="stop17992" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15569"><stop
style="stop-color:#8ea8ad;stop-opacity:1;"
offset="0"
id="stop15565" /><stop
style="stop-color:#e9e7eb;stop-opacity:1;"
offset="1"
id="stop15567" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15557"><stop
style="stop-color:#9b0e11;stop-opacity:1;"
offset="0"
id="stop15553" /><stop
style="stop-color:#370707;stop-opacity:1;"
offset="1"
id="stop15555" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15557"
id="linearGradient15559"
x1="120.06672"
y1="63.25761"
x2="135.16347"
y2="78.078682"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15569"
id="linearGradient15571"
x1="127.97037"
y1="101.66144"
x2="133.88971"
y2="104.77026"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient17996"
id="linearGradient17998"
x1="117.9284"
y1="86.055084"
x2="130.67392"
y2="76.945541"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient19524"
id="linearGradient19528"
x1="130.98172"
y1="82.402977"
x2="135.72115"
y2="86.45166"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient36672"
id="linearGradient36674"
x1="63.797714"
y1="74.81752"
x2="96.636673"
y2="120.29293"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient39095"
id="linearGradient39097"
x1="120.54506"
y1="124.76902"
x2="128.04152"
y2="126.0704"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient40554"
id="linearGradient40556"
x1="113.84585"
y1="114.04285"
x2="119.65858"
y2="128.50244"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient46361"
id="linearGradient46363"
x1="73.993439"
y1="114.13906"
x2="94.845322"
y2="71.247383"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient69598"
id="linearGradient69600"
x1="95.854446"
y1="114.66749"
x2="103.77842"
y2="120.1887"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71920"
id="linearGradient71922"
x1="98.580376"
y1="87.186958"
x2="118.09738"
y2="101.19449"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71928"
id="linearGradient71930"
x1="78.278267"
y1="97.433273"
x2="92.313202"
y2="104.33479"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient72198"
id="linearGradient72200"
x1="125.76636"
y1="138.46817"
x2="123.3327"
y2="126.03291"
gradientUnits="userSpaceOnUse" /></defs><g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="background"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><rect
style="display:inline;fill:#ccd6d7;fill-opacity:1;stroke:none;stroke-width:4.13755;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.2"
id="rect72067"
width="99.481979"
height="94.999512"
x="51.476147"
y="44.680138" /></g><g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="shadow"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient72200);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.146049,134.73858 c 0,0 11.721038,2.48294 17.938661,2.91673 6.21763,0.43378 14.75251,0.59994 22.41237,-0.43379 8.01008,-1.081 13.19907,-2.22733 14.50043,-2.66112 1.30136,-0.43379 4.00784,-1.24297 4.15244,-2.25514 0.1446,-1.01217 -3.4703,-2.74733 -6.21763,-3.32571 -2.74732,-0.57838 -12.72444,-1.44596 -14.89337,-1.44596 -2.16894,0 -37.892901,7.20499 -37.892901,7.20499 z"
id="path72192"
sodipodi:nodetypes="cssssssc" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Origami"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient40556);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.460552,134.86721 35.165798,-6.85679 16.15467,-42.7383 z"
id="path960"
sodipodi:nodetypes="cccc" /><path
style="fill:url(#linearGradient15571);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.71493,85.428056 0.3984,45.049024 -9.66213,-20.46173 z"
id="path964"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient39097);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.60518,128.00293 16.5337,2.48693 -9.68769,-20.5512 z"
id="path966"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient15559);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 118.42209,57.022622 12.70423,-2.766809 -0.0785,25.087175 -12.43878,-13.376518 z"
id="path968"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient19528);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.72114,85.386768 -4.84219,-6.459493 0.0305,11.126604 z"
id="path970"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient17998);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.10273,65.682415 11.96883,13.44935 -0.0899,10.819868 -11.88577,11.430427 z"
id="path972"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient36674);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 62.145635,130.15618 62.043392,65.435258 c 0,0 0.179321,-2.778132 1.89516,-4.036097 1.874923,-1.374597 4.341768,-1.894096 4.341768,-1.894096 l 50.91788,-11.349167 -0.0113,53.144272 -34.733274,33.56547 z"
id="path958"
sodipodi:nodetypes="ccsccccc" /></g><g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Letter"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient69600);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 94.780881,91.406803 16.870379,17.074877 -23.723345,23.00249 -18.122131,-17.99816 5.497473,-2.80607 18.404054,-0.0511 2.35163,-8.23071 z"
id="path54894"
sodipodi:nodetypes="cccccccc" /><path
style="display:inline;fill:url(#linearGradient71930);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 72.440405,92.224764 16.15467,15.745686 4.089788,-6.79927 z"
id="path54892" /><path
style="display:inline;fill:url(#linearGradient71922);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 1.124691,-14.109776 22.92453,22.286787 0.008,8.164604 -3.28863,3.16649 z"
id="path54890"
sodipodi:nodetypes="cccccc"
inkscape:label="path54890" /><path
style="display:inline;fill:url(#linearGradient46363);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 h 1.226936 l -0.05112,-14.109776 c 0,0 -5.776827,-3.220709 -12.167126,-2.40275 -6.390296,0.817957 -8.151582,2.1248 -10.58233,4.396523 -1.90229,1.777838 -2.913974,3.527446 -3.987546,7.157132 -0.512646,1.733226 -0.281963,5.988892 0.613471,8.537436 0.664591,1.891528 2.453873,4.294281 4.958868,6.134686 2.662335,1.956002 8.281825,3.527443 8.281825,3.527443 0,0 5.134614,1.887351 5.572338,4.294281 0.308137,1.69437 -0.102243,3.22071 -1.635914,4.95887 -1.258314,1.42609 -3.62969,1.99377 -6.288054,1.07357 -2.658364,-0.92021 -6.139514,-3.85065 -7.106009,-4.90775 -0.817958,-0.89464 -2.820665,-3.06173 -2.890231,-4.294021 -0.01209,-0.214205 -1.229505,-0.0963 -1.229505,-0.0963 l -0.0723,14.256941 5.879073,2.24938 c 0,0 5.214483,1.78929 8.946415,1.43143 3.731934,-0.35786 7.617235,-0.51122 11.604778,-5.16336 3.987542,-4.65213 3.680812,-12.831715 2.147141,-15.899056 -1.533673,-3.067344 -3.561212,-6.138812 -10.480087,-8.281826 -5.776829,-1.789283 -7.872846,-3.01622 -8.128458,-4.396524 -0.255611,-1.380305 0.0091,-4.253646 2.760607,-5.214481 3.220711,-1.124693 5.623462,-0.05112 7.05489,1.12469 1.431425,1.175817 5.572339,5.623462 5.572339,5.623462 z"
id="path46355"
sodipodi:nodetypes="cccssssscssssscccssssssscc" /></g></svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SPdfApplication {
public static void main(String[] args) {
SpringApplication.run(SPdfApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(SPdfApplication.class, args);
}
}

View File

@@ -0,0 +1,35 @@
package stirling.software.SPDF.config;
import java.util.Locale;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
@Configuration
public class Beans implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}

View File

@@ -1,15 +1,9 @@
package stirling.software.SPDF.controller;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -24,51 +18,51 @@ import com.spire.pdf.PdfPageBase;
import com.spire.pdf.exporting.PdfImageInfo;
import com.spire.pdf.graphics.PdfBitmap;
import stirling.software.SPDF.utils.ErrorUtils;
import stirling.software.SPDF.utils.PdfUtils;
//import com.spire.pdf.*;
@Controller
public class CompressController {
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
private static final Logger logger = LoggerFactory.getLogger(CompressController.class);
@GetMapping("/compress-pdf")
public String compressPdfForm(Model model) {
model.addAttribute("currentPage", "compress-pdf");
return "compress-pdf";
}
@GetMapping("/compress-pdf")
public String compressPdfForm(Model model) {
model.addAttribute("currentPage", "compress-pdf");
return "compress-pdf";
}
@PostMapping("/compress-pdf")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam("imageCompressionLevel") String imageCompressionLevel) throws IOException {
@PostMapping("/compress-pdf")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("imageCompressionLevel") String imageCompressionLevel)
throws IOException {
// Load a sample PDF document
PdfDocument document = new PdfDocument();
document.loadFromBytes(pdfFile.getBytes());
// Load a sample PDF document
PdfDocument document = new PdfDocument();
document.loadFromBytes(pdfFile.getBytes());
// Compress PDF
document.getFileInfo().setIncrementalUpdate(false);
document.setCompressionLevel(PdfCompressionLevel.Best);
// Compress PDF
document.getFileInfo().setIncrementalUpdate(false);
document.setCompressionLevel(PdfCompressionLevel.Best);
// compress PDF Images
for (int i = 0; i < document.getPages().getCount(); i++) {
// compress PDF Images
for (int i = 0; i < document.getPages().getCount(); i++) {
PdfPageBase page = document.getPages().get(i);
PdfImageInfo[] images = page.getImagesInfo();
if (images != null && images.length > 0)
for (int j = 0; j < images.length; j++) {
PdfImageInfo image = images[j];
PdfBitmap bp = new PdfBitmap(image.getImage());
// bp.setPngDirectToJpeg(true);
bp.setQuality(Integer.valueOf(imageCompressionLevel));
PdfPageBase page = document.getPages().get(i);
PdfImageInfo[] images = page.getImagesInfo();
if (images != null && images.length > 0)
for (int j = 0; j < images.length; j++) {
PdfImageInfo image = images[j];
PdfBitmap bp = new PdfBitmap(image.getImage());
// bp.setPngDirectToJpeg(true);
bp.setQuality(Integer.valueOf(imageCompressionLevel));
page.replaceImage(j, bp);
page.replaceImage(j, bp);
}
}
}
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_compressed.pdf");
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_compressed.pdf");
}
}
}

View File

@@ -24,54 +24,52 @@ import org.springframework.web.multipart.MultipartFile;
@Controller
public class MergeController {
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
private static final Logger logger = LoggerFactory.getLogger(MergeController.class);
@GetMapping("/merge-pdfs")
public String hello(Model model) {
model.addAttribute("currentPage", "merge-pdfs");
return "merge-pdfs";
}
@GetMapping("/merge-pdfs")
public String hello(Model model) {
model.addAttribute("currentPage", "merge-pdfs");
return "merge-pdfs";
}
@PostMapping("/merge-pdfs")
public ResponseEntity<InputStreamResource> mergePdfs(@RequestParam("fileInput") MultipartFile[] files)
throws IOException {
// Read the input PDF files into PDDocument objects
List<PDDocument> documents = new ArrayList<>();
@PostMapping("/merge-pdfs")
public ResponseEntity<InputStreamResource> mergePdfs(@RequestParam("fileInput") MultipartFile[] files) throws IOException {
// Read the input PDF files into PDDocument objects
List<PDDocument> documents = new ArrayList<>();
// Loop through the files array and read each file into a PDDocument
for (MultipartFile file : files) {
documents.add(PDDocument.load(file.getInputStream()));
}
// Loop through the files array and read each file into a PDDocument
for (MultipartFile file : files) {
documents.add(PDDocument.load(file.getInputStream()));
}
PDDocument mergedDoc = mergeDocuments(documents);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mergedDoc.save(byteArrayOutputStream);
mergedDoc.close();
PDDocument mergedDoc = mergeDocuments(documents);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mergedDoc.save(byteArrayOutputStream);
mergedDoc.close();
// Create an InputStreamResource from the merged PDF
InputStreamResource resource = new InputStreamResource(
new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
// Create an InputStreamResource from the merged PDF
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
// Return the merged PDF as a response
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource);
}
// Return the merged PDF as a response
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(resource);
}
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
// Create a new empty document
PDDocument mergedDoc = new PDDocument();
private PDDocument mergeDocuments(List<PDDocument> documents) throws IOException {
// Create a new empty document
PDDocument mergedDoc = new PDDocument();
// Iterate over the list of documents and add their pages to the merged document
for (PDDocument doc : documents) {
// Get all pages from the current document
PDPageTree pages = doc.getPages();
// Iterate over the pages and add them to the merged document
for (PDPage page : pages) {
mergedDoc.addPage(page);
}
}
// Iterate over the list of documents and add their pages to the merged document
for (PDDocument doc : documents) {
// Get all pages from the current document
PDPageTree pages = doc.getPages();
// Iterate over the pages and add them to the merged document
for (PDPage page : pages) {
mergedDoc.addPage(page);
}
}
// Return the merged document
return mergedDoc;
}
// Return the merged document
return mergedDoc;
}
}

View File

@@ -4,9 +4,7 @@ import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -20,27 +18,26 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class OverlayImageController {
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
private static final Logger logger = LoggerFactory.getLogger(OverlayImageController.class);
@GetMapping("/add-image")
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image");
return "add-image";
}
@GetMapping("/add-image")
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image");
return "add-image";
}
@PostMapping("/add-image")
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x,
@RequestParam("y") float y) {
try {
byte[] pdfBytes = pdfFile.getBytes();
byte[] imageBytes = imageFile.getBytes();
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y);
@PostMapping("/add-image")
public ResponseEntity<byte[]> overlayImage(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("fileInput2") MultipartFile imageFile, @RequestParam("x") float x,
@RequestParam("y") float y) {
try {
byte[] pdfBytes = pdfFile.getBytes();
byte[] imageBytes = imageFile.getBytes();
byte[] result = PdfUtils.overlayImage(pdfBytes, imageBytes, x, y);
return PdfUtils.bytesToWebResponse(result, pdfFile.getName() + "_overlayed.pdf");
} catch (IOException e) {
logger.error("Failed to add image to PDF", e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
return PdfUtils.bytesToWebResponse(result, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_overlayed.pdf");
} catch (IOException e) {
logger.error("Failed to add image to PDF", e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
}

View File

@@ -1,43 +1,25 @@
package stirling.software.SPDF.controller;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class PdfController {
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
private static final Logger logger = LoggerFactory.getLogger(PdfController.class);
@GetMapping("/home")
public String root(Model model) {
return "redirect:/";
}
@GetMapping("/home")
public String root(Model model) {
return "redirect:/";
}
@GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");
return "home";
}
@GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");
return "home";
}
}

View File

@@ -1,6 +1,5 @@
package stirling.software.SPDF.controller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -9,9 +8,6 @@ import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -25,104 +21,102 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class RearrangePagesPDFController {
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
private static final Logger logger = LoggerFactory.getLogger(RearrangePagesPDFController.class);
@GetMapping("/pdf-organizer")
public String pageOrganizer(Model model) {
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer";
}
@GetMapping("/pdf-organizer")
public String pageOrganizer(Model model) {
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer";
}
@GetMapping("/delete-pages")
public String pageDeleter(Model model) {
model.addAttribute("currentPage", "delete-pages");
return "delete-pages";
}
@GetMapping("/remove-pages")
public String pageDeleter(Model model) {
model.addAttribute("currentPage", "remove-pages");
return "remove-pages";
}
@PostMapping("/delete-pages")
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam("pagesToDelete") String pagesToDelete) throws IOException {
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(",");
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
int pageIndex = pagesToRemove.get(i);
document.removePage(pageIndex);
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_removed_pages.pdf");
@PostMapping("/remove-pages")
public ResponseEntity<byte[]> deletePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pagesToDelete") String pagesToDelete) throws IOException {
}
PDDocument document = PDDocument.load(pdfFile.getBytes());
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
List<Integer> newPageOrder = new ArrayList<Integer>();
// loop through the page order array
for (String element : pageOrderArr) {
// check if the element contains a range of pages
if (element.contains("-")) {
// split the range into start and end page
String[] range = element.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
// check if the end page is greater than total pages
if (end > totalPages) {
end = totalPages;
}
// loop through the range of pages
for (int j = start; j <= end; j++) {
// print the current index
newPageOrder.add(j - 1);
}
} else {
// if the element is a single page
newPageOrder.add(Integer.parseInt(element) - 1);
}
}
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pagesToDelete.split(",");
return newPageOrder;
}
List<Integer> pagesToRemove = pageOrderToString(pageOrderArr, document.getNumberOfPages());
@PostMapping("/rearrange-pages")
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam("pageOrder") String pageOrder) {
try {
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
for (int i = pagesToRemove.size() - 1; i >= 0; i--) {
int pageIndex = pagesToRemove.get(i);
document.removePage(pageIndex);
}
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder.split(",");
// int[] newPageOrder = new int[pageOrderArr.length];
int totalPages = document.getNumberOfPages();
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_removed_pages.pdf");
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int i = 0; i < newPageOrder.size(); i++) {
newPages.add(document.getPage(newPageOrder.get(i)));
}
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
}
private List<Integer> pageOrderToString(String[] pageOrderArr, int totalPages) {
List<Integer> newPageOrder = new ArrayList<>();
// loop through the page order array
for (String element : pageOrderArr) {
// check if the element contains a range of pages
if (element.contains("-")) {
// split the range into start and end page
String[] range = element.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
// check if the end page is greater than total pages
if (end > totalPages) {
end = totalPages;
}
// loop through the range of pages
for (int j = start; j <= end; j++) {
// print the current index
newPageOrder.add(j - 1);
}
} else {
// if the element is a single page
newPageOrder.add(Integer.parseInt(element) - 1);
}
}
// Add the pages in the new order
for (PDPage page : newPages) {
document.addPage(page);
}
return newPageOrder;
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rearranged.pdf");
} catch (IOException e) {
@PostMapping("/rearrange-pages")
public ResponseEntity<byte[]> rearrangePages(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("pageOrder") String pageOrder) {
try {
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
logger.error("Failed rearranging documents", e);
return null;
}
}
// Split the page order string into an array of page numbers or range of numbers
String[] pageOrderArr = pageOrder.split(",");
// int[] newPageOrder = new int[pageOrderArr.length];
int totalPages = document.getNumberOfPages();
List<Integer> newPageOrder = pageOrderToString(pageOrderArr, totalPages);
// Create a new list to hold the pages in the new order
List<PDPage> newPages = new ArrayList<>();
for (int i = 0; i < newPageOrder.size(); i++) {
newPages.add(document.getPage(newPageOrder.get(i)));
}
// Remove all the pages from the original document
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
document.removePage(i);
}
// Add the pages in the new order
for (PDPage page : newPages) {
document.addPage(page);
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rearranged.pdf");
} catch (IOException e) {
logger.error("Failed rearranging documents", e);
return null;
}
}
}

View File

@@ -1,19 +1,12 @@
package stirling.software.SPDF.controller;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.ListIterator;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -27,34 +20,29 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class RotationController {
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
private static final Logger logger = LoggerFactory.getLogger(RotationController.class);
@GetMapping("/rotate-pdf")
public String rotatePdfForm(Model model) {
model.addAttribute("currentPage", "rotate-pdf");
return "rotate-pdf";
}
@GetMapping("/rotate-pdf")
public String rotatePdfForm(Model model) {
model.addAttribute("currentPage", "rotate-pdf");
return "rotate-pdf";
}
@PostMapping("/rotate-pdf")
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam("angle") String angle) throws IOException {
@PostMapping("/rotate-pdf")
public ResponseEntity<byte[]> rotatePDF(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("angle") Integer angle) throws IOException {
// Load the PDF document
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Load the PDF document
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Get the list of pages in the document
PDPageTree pages = document.getPages();
// Get the list of pages in the document
PDPageTree pages = document.getPages();
// Rotate all pages by the specified angle
Iterator<PDPage> iterPage = pages.iterator();
for (PDPage page : pages) {
page.setRotation(page.getRotation() + angle);
}
while (iterPage.hasNext()) {
PDPage page = iterPage.next();
page.setRotation(Integer.valueOf(angle));
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_rotated.pdf");
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_rotated.pdf");
}
}
}

View File

@@ -37,106 +37,104 @@ import org.springframework.web.multipart.MultipartFile;
@Controller
public class SplitPDFController {
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
private static final Logger logger = LoggerFactory.getLogger(SplitPDFController.class);
@GetMapping("/split-pdfs")
public String splitPdfForm(Model model) {
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs";
}
@GetMapping("/split-pdfs")
public String splitPdfForm(Model model) {
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs";
}
@PostMapping("/split-pages")
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file,
@RequestParam("pages") String pages) throws IOException {
// parse user input
@PostMapping("/split-pages")
public ResponseEntity<Resource> splitPdf(@RequestParam("fileInput") MultipartFile file, @RequestParam("pages") String pages) throws IOException {
// parse user input
// open the pdf document
InputStream inputStream = file.getInputStream();
PDDocument document = PDDocument.load(inputStream);
// open the pdf document
InputStream inputStream = file.getInputStream();
PDDocument document = PDDocument.load(inputStream);
List<Integer> pageNumbers = new ArrayList<>();
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
if (pages.toLowerCase().equals("all")) {
for (int i = 0; i < document.getNumberOfPages(); i++) {
pageNumbers.add(i);
}
} else {
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
String lastpage = String.valueOf(document.getNumberOfPages());
pageNumbersStr.add(lastpage);
}
for (String page : pageNumbersStr) {
if (page.contains("-")) {
String[] range = page.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
for (int i = start; i <= end; i++) {
pageNumbers.add(i);
}
} else {
pageNumbers.add(Integer.parseInt(page));
}
}
}
List<Integer> pageNumbers = new ArrayList<>();
pages = pages.replaceAll("\\s+", ""); // remove whitespaces
if (pages.toLowerCase().equals("all")) {
for (int i = 0; i < document.getNumberOfPages(); i++) {
pageNumbers.add(i);
}
} else {
List<String> pageNumbersStr = new ArrayList<>(Arrays.asList(pages.split(",")));
if (!pageNumbersStr.contains(String.valueOf(document.getNumberOfPages()))) {
String lastpage = String.valueOf(document.getNumberOfPages());
pageNumbersStr.add(lastpage);
}
for (String page : pageNumbersStr) {
if (page.contains("-")) {
String[] range = page.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
for (int i = start; i <= end; i++) {
pageNumbers.add(i);
}
} else {
pageNumbers.add(Integer.parseInt(page));
}
}
}
logger.info("Splitting PDF into pages: {}",
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
logger.info("Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
// split the document
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
int currentPage = 0;
for (int pageNumber : pageNumbers) {
try (PDDocument splitDocument = new PDDocument()) {
for (int i = currentPage; i < pageNumber; i++) {
PDPage page = document.getPage(i);
splitDocument.addPage(page);
logger.debug("Adding page {} to split document", i);
}
currentPage = pageNumber;
logger.debug("Setting current page to {}", currentPage);
// split the document
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
int currentPage = 0;
for (int pageNumber : pageNumbers) {
try (PDDocument splitDocument = new PDDocument()) {
for (int i = currentPage; i < pageNumber; i++) {
PDPage page = document.getPage(i);
splitDocument.addPage(page);
logger.debug("Adding page {} to split document", i);
}
currentPage = pageNumber;
logger.debug("Setting current page to {}", currentPage);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
splitDocument.save(baos);
splitDocumentsBoas.add(baos);
} catch (Exception e) {
logger.error("Failed splitting documents and saving them", e);
throw e;
}
}
splitDocumentsBoas.add(baos);
} catch (Exception e) {
logger.error("Failed splitting documents and saving them", e);
throw e;
}
}
// closing the original document
document.close();
// closing the original document
document.close();
// create the zip file
Path zipFile = Paths.get("split_documents.zip");
URI uri = URI.create("jar:file:" + zipFile.toUri().getPath());
Map<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystem zipfs = FileSystems.newFileSystem(uri, env);
// create the zip file
Path zipFile = Paths.get("split_documents.zip");
URI uri = URI.create("jar:file:" + zipFile.toUri().getPath());
Map<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystem zipfs = FileSystems.newFileSystem(uri, env);
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = "split_document_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
Path pathInZipfile = zipfs.getPath(fileName);
try (OutputStream os = Files.newOutputStream(pathInZipfile)) {
os.write(pdf);
logger.info("Wrote split document {} to zip file", fileName);
} catch (Exception e) {
logger.error("Failed writing to zip", e);
throw e;
}
}
zipfs.close();
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
ByteArrayResource resource = new ByteArrayResource(data);
new File("split_documents.zip").delete();
// return the Resource in the response
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip")
.contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(resource.contentLength()).body(resource);
}
// loop through the split documents and write them to the zip file
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
String fileName = "split_document_" + (i + 1) + ".pdf";
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
byte[] pdf = baos.toByteArray();
Path pathInZipfile = zipfs.getPath(fileName);
try (OutputStream os = Files.newOutputStream(pathInZipfile)) {
os.write(pdf);
logger.info("Wrote split document {} to zip file", fileName);
} catch (Exception e) {
logger.error("Failed writing to zip", e);
throw e;
}
}
zipfs.close();
logger.info("Successfully created zip file with split documents: {}", zipFile.toString());
byte[] data = Files.readAllBytes(zipFile);
ByteArrayResource resource = new ByteArrayResource(data);
new File("split_documents.zip").delete();
// return the Resource in the response
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(resource.contentLength()).body(resource);
}
}

View File

@@ -2,8 +2,11 @@ package stirling.software.SPDF.controller.converters;
import java.io.IOException;
import org.apache.pdfbox.rendering.ImageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -20,51 +23,75 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class ConvertImgPDFController {
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
private static final Logger logger = LoggerFactory.getLogger(ConvertImgPDFController.class);
@GetMapping("/img-to-pdf")
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "img-to-pdf");
return "convert/img-to-pdf";
}
@GetMapping("/img-to-pdf")
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "img-to-pdf");
return "convert/img-to-pdf";
}
@GetMapping("/pdf-to-img")
public String pdfToimgForm(Model model) {
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
@GetMapping("/pdf-to-img")
public String pdfToimgForm(Model model) {
model.addAttribute("currentPage", "pdf-to-img");
return "convert/pdf-to-img";
}
@PostMapping("/img-to-pdf")
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
// Convert the file to PDF and get the resulting bytes
byte[] bytes = PdfUtils.convertToPdf(file.getInputStream());
logger.info("File {} successfully converted to pdf", file.getOriginalFilename());
@PostMapping("/img-to-pdf")
public ResponseEntity<byte[]> convertToPdf(@RequestParam("fileInput") MultipartFile file) throws IOException {
// Convert the file to PDF and get the resulting bytes
byte[] bytes = PdfUtils.convertToPdf(file.getInputStream());
logger.info("File {} successfully converted to pdf", file.getOriginalFilename());
return PdfUtils.bytesToWebResponse(bytes, file.getName() + "_coverted.pdf");
}
return PdfUtils.bytesToWebResponse(bytes, file.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_coverted.pdf");
}
@PostMapping("/pdf-to-img")
public ResponseEntity<byte[]> convertToImage(@RequestParam("fileInput") MultipartFile file,
@RequestParam("imageFormat") String imageFormat) throws IOException {
byte[] pdfBytes = file.getBytes();
// returns bytes for image
byte[] result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> response = new ResponseEntity<>(result, headers, HttpStatus.OK);
return response;
}
@PostMapping("/pdf-to-img")
public ResponseEntity<Resource> convertToImage(@RequestParam("fileInput") MultipartFile file, @RequestParam("imageFormat") String imageFormat,
@RequestParam("singleOrMultiple") String singleOrMultiple, @RequestParam("colorType") String colorType, @RequestParam("dpi") String dpi) throws IOException {
private String getMediaType(String imageFormat) {
if (imageFormat.equalsIgnoreCase("PNG"))
return "image/png";
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
return "image/jpeg";
else if (imageFormat.equalsIgnoreCase("GIF"))
return "image/gif";
else
return "application/octet-stream";
}
byte[] pdfBytes = file.getBytes();
ImageType colorTypeResult = ImageType.RGB;
if ("greyscale".equals(colorType)) {
colorTypeResult = ImageType.GRAY;
} else if ("blackwhite".equals(colorType)) {
colorTypeResult = ImageType.BINARY;
}
// returns bytes for image
boolean singleImage = singleOrMultiple.equals("single");
byte[] result = null;
try {
result = PdfUtils.convertFromPdf(pdfBytes, imageFormat.toLowerCase(), colorTypeResult, singleImage, Integer.valueOf(dpi));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (singleImage) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat)));
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<Resource> response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK);
return response;
} else {
ByteArrayResource resource = new ByteArrayResource(result);
// return the Resource in the response
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=converted_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(resource.contentLength()).body(resource);
}
}
private String getMediaType(String imageFormat) {
if (imageFormat.equalsIgnoreCase("PNG"))
return "image/png";
else if (imageFormat.equalsIgnoreCase("JPEG") || imageFormat.equalsIgnoreCase("JPG"))
return "image/jpeg";
else if (imageFormat.equalsIgnoreCase("GIF"))
return "image/gif";
else
return "application/octet-stream";
}
}

View File

@@ -0,0 +1,81 @@
package stirling.software.SPDF.controller.converters;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.ss.usermodel.Color;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class ConvertXlsxController {
@GetMapping("/xlsx-to-pdf")
public String cinvertToPDF(Model model) {
model.addAttribute("currentPage", "xlsx-to-pdf");
return "convert/xlsx-to-pdf";
}
@PostMapping("/xlsx-to-pdf")
public ResponseEntity<byte[]> convertToPDF(@RequestParam("fileInput") MultipartFile xlsx) throws IOException, DocumentException{
// Load Excel file
Workbook workbook = WorkbookFactory.create(xlsx.getInputStream());
// Create PDF document
Document document = new Document();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter.getInstance(document, outputStream);
document.open();
// Convert each sheet in Excel to a separate page in PDF
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
PdfPTable table = new PdfPTable(workbook.getSheetAt(i).getRow(0).getPhysicalNumberOfCells());
for (int row = 0; row < workbook.getSheetAt(i).getPhysicalNumberOfRows(); row++) {
for (int cell = 0; cell < workbook.getSheetAt(i).getRow(row).getPhysicalNumberOfCells(); cell++) {
PdfPCell pdfCell = new PdfPCell();
pdfCell.addElement(new com.itextpdf.text.Paragraph(workbook.getSheetAt(i).getRow(row).getCell(cell).toString()));
// Copy cell style, borders, and background color
pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getBottomBorderColor()));
pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getTopBorderColor()));
pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getLeftBorderColor()));
pdfCell.setBorderColor(new BaseColor(workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getRightBorderColor()));
Short bc = workbook.getSheetAt(i).getRow(row).getCell(cell).getCellStyle().getFillBackgroundColor();
pdfCell.setBackgroundColor(new BaseColor(bc));
table.addCell(pdfCell);
}
}
document.add(table);
}
// Close document and output stream
document.close();
outputStream.flush();
outputStream.close();
return PdfUtils.boasToWebResponse(outputStream, xlsx.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToPDF.pdf");
// Close document and input stream
}
}

View File

@@ -0,0 +1,138 @@
package stirling.software.SPDF.controller.security;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class MetadataController {
@GetMapping("/change-metadata")
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "change-metadata");
return "security/change-metadata";
}
private String checkUndefined(String entry) {
// Check if the string is "undefined"
if("undefined".equals(entry)) {
// Return null if it is
return null;
}
// Return the original string if it's not "undefined"
return entry;
}
@PostMapping("/update-metadata")
public ResponseEntity<byte[]> metadata(@RequestParam("fileInput") MultipartFile pdfFile,
@RequestParam(value = "deleteAll", required = false, defaultValue = "false") Boolean deleteAll, @RequestParam(value = "author", required = false) String author,
@RequestParam(value = "creationDate", required = false) String creationDate, @RequestParam(value = "creator", required = false) String creator,
@RequestParam(value = "keywords", required = false) String keywords, @RequestParam(value = "modificationDate", required = false) String modificationDate,
@RequestParam(value = "producer", required = false) String producer, @RequestParam(value = "subject", required = false) String subject,
@RequestParam(value = "title", required = false) String title, @RequestParam(value = "trapped", required = false) String trapped,
@RequestParam Map<String, String> allRequestParams) throws IOException {
// Load the PDF file into a PDDocument
PDDocument document = PDDocument.load(pdfFile.getBytes());
// Get the document information from the PDF
PDDocumentInformation info = document.getDocumentInformation();
// Check if each metadata value is "undefined" and set it to null if it is
author = checkUndefined(author);
creationDate = checkUndefined(creationDate);
creator = checkUndefined(creator);
keywords = checkUndefined(keywords);
modificationDate = checkUndefined(modificationDate);
producer = checkUndefined(producer);
subject = checkUndefined(subject);
title = checkUndefined(title);
trapped = checkUndefined(trapped);
// If the "deleteAll" flag is set, remove all metadata from the document information
if (deleteAll) {
for (String key : info.getMetadataKeys()) {
info.setCustomMetadataValue(key, null);
}
// Remove metadata from the PDF history
document.getDocumentCatalog().getCOSObject().removeItem(COSName.getPDFName("Metadata"));
document.getDocumentCatalog().getCOSObject().removeItem(COSName.getPDFName("PieceInfo"));
author = null;
creationDate = null;
creator = null;
keywords = null;
modificationDate = null;
producer = null;
subject = null;
title = null;
trapped = null;
} else {
// Iterate through the request parameters and set the metadata values
for (Entry<String, String> entry : allRequestParams.entrySet()) {
String key = entry.getKey();
// Check if the key is a standard metadata key
if (!key.equalsIgnoreCase("Author") && !key.equalsIgnoreCase("CreationDate") && !key.equalsIgnoreCase("Creator") && !key.equalsIgnoreCase("Keywords")
&& !key.equalsIgnoreCase("modificationDate") && !key.equalsIgnoreCase("Producer") && !key.equalsIgnoreCase("Subject") && !key.equalsIgnoreCase("Title")
&& !key.equalsIgnoreCase("Trapped") && !key.contains("customKey") && !key.contains("customValue")) {
info.setCustomMetadataValue(key, entry.getValue());
} else if (key.contains("customKey")) {
int number = Integer.parseInt(key.replaceAll("\\D", ""));
String customKey = entry.getValue();
String customValue = allRequestParams.get("customValue" + number);
info.setCustomMetadataValue(customKey, customValue);
}
}
}
if (creationDate != null && creationDate.length() > 0) {
Calendar creationDateCal = Calendar.getInstance();
try {
creationDateCal.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(creationDate));
} catch (ParseException e) {
e.printStackTrace();
}
info.setCreationDate(creationDateCal);
} else {
info.setCreationDate(null);
}
if (modificationDate != null && modificationDate.length() > 0) {
Calendar modificationDateCal = Calendar.getInstance();
try {
modificationDateCal.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(modificationDate));
} catch (ParseException e) {
e.printStackTrace();
}
info.setModificationDate(modificationDateCal);
} else {
info.setModificationDate(null);
}
info.setCreator(creator);
info.setKeywords(keywords);
info.setAuthor(author);
info.setProducer(producer);
info.setSubject(subject);
info.setTitle(title);
info.setTrapped(trapped);
document.setDocumentInformation(info);
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_metadata.pdf");
}
}

View File

@@ -1,18 +1,12 @@
package stirling.software.SPDF.controller.security;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -26,67 +20,62 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class PasswordController {
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
private static final Logger logger = LoggerFactory.getLogger(PasswordController.class);
@GetMapping("/add-password")
public String addPasswordForm(Model model) {
model.addAttribute("currentPage", "add-password");
return "security/add-password";
}
@GetMapping("/add-password")
public String addPasswordForm(Model model) {
model.addAttribute("currentPage", "add-password");
return "security/add-password";
}
@GetMapping("/remove-password")
public String removePasswordForm(Model model) {
model.addAttribute("currentPage", "remove-password");
return "security/remove-password";
}
@GetMapping("/remove-password")
public String removePasswordForm(Model model) {
model.addAttribute("currentPage", "remove-password");
return "security/remove-password";
}
@GetMapping("/change-permissions")
public String permissionsForm(Model model) {
model.addAttribute("currentPage", "change-permissions");
return "security/change-permissions";
}
@GetMapping("/change-permissions")
public String permissionsForm(Model model) {
model.addAttribute("currentPage", "change-permissions");
return "security/change-permissions";
}
@PostMapping("/remove-password")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput,
@RequestParam(name = "password") String password) throws IOException {
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
document.setAllSecurityToBeRemoved(true);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_password_removed.pdf");
}
@PostMapping("/remove-password")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(name = "password") String password) throws IOException {
PDDocument document = PDDocument.load(fileInput.getBytes(), password);
document.setAllSecurityToBeRemoved(true);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_password_removed.pdf");
}
@PostMapping("/add-password")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput,
@RequestParam(defaultValue = "", name = "password") String password,
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength,
@RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm,
@RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint,
@RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
throws IOException {
@PostMapping("/add-password")
public ResponseEntity<byte[]> compressPDF(@RequestParam("fileInput") MultipartFile fileInput, @RequestParam(defaultValue = "", name = "password") String password,
@RequestParam(defaultValue = "128", name = "keyLength") int keyLength, @RequestParam(defaultValue = "false", name = "canAssembleDocument") boolean canAssembleDocument,
@RequestParam(defaultValue = "false", name = "canExtractContent") boolean canExtractContent,
@RequestParam(defaultValue = "false", name = "canExtractForAccessibility") boolean canExtractForAccessibility,
@RequestParam(defaultValue = "false", name = "canFillInForm") boolean canFillInForm, @RequestParam(defaultValue = "false", name = "canModify") boolean canModify,
@RequestParam(defaultValue = "false", name = "canModifyAnnotations") boolean canModifyAnnotations,
@RequestParam(defaultValue = "false", name = "canPrint") boolean canPrint, @RequestParam(defaultValue = "false", name = "canPrintFaithful") boolean canPrintFaithful)
throws IOException {
PDDocument document = PDDocument.load(fileInput.getBytes());
AccessPermission ap = new AccessPermission();
PDDocument document = PDDocument.load(fileInput.getBytes());
AccessPermission ap = new AccessPermission();
ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent);
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
ap.setCanFillInForm(!canFillInForm);
ap.setCanModify(!canModify);
ap.setCanModifyAnnotations(!canModifyAnnotations);
ap.setCanPrint(!canPrint);
ap.setCanPrintFaithful(!canPrintFaithful);
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
spp.setEncryptionKeyLength(keyLength);
ap.setCanAssembleDocument(!canAssembleDocument);
ap.setCanExtractContent(!canExtractContent);
ap.setCanExtractForAccessibility(!canExtractForAccessibility);
ap.setCanFillInForm(!canFillInForm);
ap.setCanModify(!canModify);
ap.setCanModifyAnnotations(!canModifyAnnotations);
ap.setCanPrint(!canPrint);
ap.setCanPrintFaithful(!canPrintFaithful);
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
spp.setEncryptionKeyLength(keyLength);
spp.setPermissions(ap);
spp.setPermissions(ap);
document.protect(spp);
document.protect(spp);
return PdfUtils.pdfDocToWebResponse(document, fileInput.getName() + "_passworded.pdf");
}
return PdfUtils.pdfDocToWebResponse(document, fileInput.getOriginalFilename().replaceFirst("[.][^.]+$", "")+ "_passworded.pdf");
}
}

View File

@@ -1,7 +1,6 @@
package stirling.software.SPDF.controller.security;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -9,11 +8,7 @@ import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -27,57 +22,53 @@ import stirling.software.SPDF.utils.PdfUtils;
@Controller
public class WatermarkController {
@GetMapping("/add-watermark")
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "add-watermark");
return "security/add-watermark";
}
@GetMapping("/add-watermark")
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "add-watermark");
return "security/add-watermark";
}
@PostMapping("/add-watermark")
public ResponseEntity<byte[]> addWatermark(@RequestParam("pdfFile") MultipartFile pdfFile,
@RequestParam("watermarkText") String watermarkText,
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize,
@RequestParam(defaultValue = "0", name = "rotation") float rotation,
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer,
@RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer) throws IOException {
@PostMapping("/add-watermark")
public ResponseEntity<byte[]> addWatermark(@RequestParam("fileInput") MultipartFile pdfFile, @RequestParam("watermarkText") String watermarkText,
@RequestParam(defaultValue = "30", name = "fontSize") float fontSize, @RequestParam(defaultValue = "0", name = "rotation") float rotation,
@RequestParam(defaultValue = "50", name = "widthSpacer") int widthSpacer, @RequestParam(defaultValue = "50", name = "heightSpacer") int heightSpacer)
throws IOException {
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
// Load the input PDF
PDDocument document = PDDocument.load(pdfFile.getInputStream());
// Create a page in the document
for (PDPage page : document.getPages()) {
// Get the page's content stream
PDPageContentStream contentStream = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.APPEND, true);
// Create a page in the document
for (PDPage page : document.getPages()) {
// Get the page's content stream
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true);
// Set font of watermark
PDFont font = PDType1Font.HELVETICA_BOLD;
contentStream.beginText();
contentStream.setFont(font, fontSize);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
// Set font of watermark
PDFont font = PDType1Font.HELVETICA_BOLD;
contentStream.beginText();
contentStream.setFont(font, fontSize);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY);
// Set size and location of watermark
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
float watermarkHeight = heightSpacer + fontSize;
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
// Set size and location of watermark
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
float watermarkWidth = widthSpacer + font.getStringWidth(watermarkText) * fontSize / 1000;
float watermarkHeight = heightSpacer + fontSize;
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
// Add the watermark text
for (int i = 0; i < watermarkRows; i++) {
for (int j = 0; j < watermarkCols; j++) {
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation),
j * watermarkWidth, i * watermarkHeight));
contentStream.showTextWithPositioning(new Object[] { watermarkText });
}
}
// Add the watermark text
for (int i = 0; i < watermarkRows; i++) {
for (int j = 0; j < watermarkCols; j++) {
contentStream.setTextMatrix(Matrix.getRotateInstance((float) Math.toRadians(rotation), j * watermarkWidth, i * watermarkHeight));
contentStream.showTextWithPositioning(new Object[] { watermarkText });
}
}
contentStream.endText();
contentStream.endText();
// Close the content stream
contentStream.close();
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getName() + "_watermarked.pdf");
}
// Close the content stream
contentStream.close();
}
return PdfUtils.pdfDocToWebResponse(document, pdfFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_watermarked.pdf");
}
}

View File

@@ -0,0 +1,32 @@
package stirling.software.SPDF.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.ui.Model;
import org.springframework.web.servlet.ModelAndView;
public class ErrorUtils {
public static Model exceptionToModel(Model model, Exception ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
model.addAttribute("errorMessage", ex.getMessage());
model.addAttribute("stackTrace", stackTrace);
return model;
}
public static ModelAndView exceptionToModelView(Model model, Exception ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMessage", ex.getMessage());
modelAndView.addObject("stackTrace", stackTrace);
return modelAndView;
}
}

View File

@@ -1,5 +1,6 @@
package stirling.software.SPDF.utils;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -7,12 +8,13 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
@@ -27,142 +29,183 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.spire.pdf.PdfDocument;
public class PdfUtils {
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
public static byte[] convertToPdf(InputStream imageStream) throws IOException {
public static byte[] convertToPdf(InputStream imageStream) throws IOException {
// Create a File object for the image
File imageFile = new File("image.jpg");
// Create a File object for the image
File imageFile = new File("image.jpg");
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) {
byte[] buffer = new byte[1024];
int len;
// Read from the input stream and write to the file
while ((len = input.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath());
} catch (IOException e) {
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
throw e;
}
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = imageStream) {
byte[] buffer = new byte[1024];
int len;
// Read from the input stream and write to the file
while ((len = input.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
logger.info("Image successfully written to file: {}", imageFile.getAbsolutePath());
} catch (IOException e) {
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
throw e;
}
try (PDDocument doc = new PDDocument()) {
// Create a new PDF page
PDPage page = new PDPage();
doc.addPage(page);
try (PDDocument doc = new PDDocument()) {
// Create a new PDF page
PDPage page = new PDPage();
doc.addPage(page);
// Create an image object from the image file
PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc);
// Create an image object from the image file
PDImageXObject image = PDImageXObject.createFromFileByContent(imageFile, doc);
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
// Draw the image onto the page
contentStream.drawImage(image, 0, 0);
logger.info("Image successfully added to PDF");
} catch (IOException e) {
logger.error("Error adding image to PDF", e);
throw e;
}
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
// Draw the image onto the page
contentStream.drawImage(image, 0, 0);
logger.info("Image successfully added to PDF");
} catch (IOException e) {
logger.error("Error adding image to PDF", e);
throw e;
}
// Create a ByteArrayOutputStream to save the PDF to
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.save(byteArrayOutputStream);
logger.info("PDF successfully saved to byte array");
return byteArrayOutputStream.toByteArray();
}
}
// Create a ByteArrayOutputStream to save the PDF to
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.save(byteArrayOutputStream);
logger.info("PDF successfully saved to byte array");
return byteArrayOutputStream.toByteArray();
}
}
public static byte[] convertFromPdf(byte[] inputStream, String imageType) throws IOException {
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
// Create a PDFRenderer to convert the PDF to an image
PDFRenderer pdfRenderer = new PDFRenderer(document);
BufferedImage bim = pdfRenderer.renderImageWithDPI(0, 300, ImageType.RGB);
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI)
throws IOException, Exception {
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
int pageCount = document.getNumberOfPages();
List<BufferedImage> images = new ArrayList<>();
// Get an ImageWriter for the specified image type
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(imageType);
ImageWriter writer = writers.next();
// Create images of all pages
for (int i = 0; i < pageCount; i++) {
images.add(pdfRenderer.renderImageWithDPI(i, 300, colorType));
}
if (singleImage) {
// Combine all images into a single big image
BufferedImage combined = new BufferedImage(images.get(0).getWidth(), images.get(0).getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);
Graphics g = combined.getGraphics();
for (int i = 0; i < images.size(); i++) {
g.drawImage(images.get(i), 0, i * images.get(0).getHeight(), null);
}
images = Arrays.asList(combined);
}
// Create a ByteArrayOutputStream to save the image to
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
writer.setOutput(ios);
// Write the image to the output stream
writer.write(new IIOImage(bim, null, null));
// Log that the image was successfully written to the byte array
logger.info("Image successfully written to byte array");
}
return baos.toByteArray();
} catch (IOException e) {
// Log an error message if there is an issue converting the PDF to an image
logger.error("Error converting PDF to image", e);
throw e;
}
}
// Create a ByteArrayOutputStream to save the image(s) to
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (singleImage) {
// Write the image to the output stream
ImageIO.write(images.get(0), "PNG", baos);
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException {
// Log that the image was successfully written to the byte array
logger.info("Image successfully written to byte array");
} else {
// Zip the images and return as byte array
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (int i = 0; i < images.size(); i++) {
BufferedImage image = images.get(i);
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
ImageIO.write(image, "PNG", baosImage);
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
// Get the first page of the PDF
PDPage page = document.getPage(0);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.APPEND, true)) {
// Create an image object from the image bytes
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
// Draw the image onto the page at the specified x and y coordinates
contentStream.drawImage(image, x, y);
logger.info("Image successfully overlayed onto PDF");
}
// Create a ByteArrayOutputStream to save the PDF to
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
logger.info("PDF successfully saved to byte array");
return baos.toByteArray();
} catch (IOException e) {
// Log an error message if there is an issue overlaying the image onto the PDF
logger.error("Error overlaying image onto PDF", e);
throw e;
}
}
// Add the image to the zip file
zos.putNextEntry(new ZipEntry(String.format("page_%d.%s", i + 1, "png")));
zos.write(baosImage.toByteArray());
}
}
// Log that the images were successfully written to the byte array
logger.info("Images successfully written to byte array as a zip");
}
}
return baos.toByteArray();
} catch (IOException e) {
// Log an error message if there is an issue converting the PDF to an image
logger.error("Error converting PDF to image", e);
throw e;
}
}
public static ResponseEntity<byte[]> pdfDocToWebResponse(PdfDocument document, String docName) throws IOException {
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y) throws IOException {
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.saveToStream(baos);
// Close the document
document.close();
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
// Get the first page of the PDF
PDPage page = document.getPage(0);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true)) {
// Create an image object from the image bytes
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
// Draw the image onto the page at the specified x and y coordinates
contentStream.drawImage(image, x, y);
logger.info("Image successfully overlayed onto PDF");
}
// Create a ByteArrayOutputStream to save the PDF to
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
logger.info("PDF successfully saved to byte array");
return baos.toByteArray();
} catch (IOException e) {
// Log an error message if there is an issue overlaying the image onto the PDF
logger.error("Error overlaying image onto PDF", e);
throw e;
}
}
return PdfUtils.boasToWebResponse(baos, docName);
}
public static ResponseEntity<byte[]> iTextDocToWebResponse(Document document, String docName) throws IOException, DocumentException {
// Close the document
document.close();
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baos).close();
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
return PdfUtils.boasToWebResponse(baos, docName);
}
public static ResponseEntity<byte[]> pdfDocToWebResponse(PdfDocument document, String docName) throws IOException {
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.saveToStream(baos);
// Close the document
document.close();
return PdfUtils.boasToWebResponse(baos, docName);
}
return PdfUtils.boasToWebResponse(baos, docName);
}
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName)
throws IOException {
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
}
// Open Byte Array and save document to it
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// Close the document
document.close();
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
return PdfUtils.boasToWebResponse(baos, docName);
}
// Return the PDF as a response
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentLength(bytes.length);
headers.setContentDispositionFormData("attachment", docName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
}
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
// Return the PDF as a response
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentLength(bytes.length);
headers.setContentDispositionFormData("attachment", docName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
}

View File

@@ -9,3 +9,9 @@ spring.servlet.multipart.max-file-size=1000MB
spring.servlet.multipart.max-request-size=1000MB
server.forward-headers-strategy=NATIVE
server.error.path=/error
server.error.whitelabel.enabled=false
server.error.include-stacktrace=always
server.error.include-exception=true
server.error.include-message=always

View File

View File

@@ -0,0 +1,234 @@
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!#
# Translated by Google Translate #
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!#
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=rtl
pdfPrompt=اختر PDF
multiPdfPrompt=اختر ملفات PDF (2+)
multiPdfDropPrompt=حدد (أو اسحب وأفلت) جميع ملفات PDF التي تحتاجها
imgPrompt=اختر صورة
genericSubmit=إرسال
processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية ما يصل إلى دقيقة حسب حجم الملف
pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
goToPage=اذهب
true=\u0635\u062D\u064A\u062D
false=\u062E\u0637\u0623
unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641
#############
# HOME-PAGE #
#############
home.desc=متجرك الشامل المستضاف محليًا لجميع احتياجات PDF الخاصة بك.
navbar.convert=تحويل
navbar.security=الأمان
navbar.other=أخرى
navbar.darkmode=الوضع الداكن
home.merge.title=دمج ملفات PDF
home.merge.desc=دمج ملفات PDF متعددة في ملف واحد بسهولة.
home.split.title=انقسام ملفات PDF
home.split.desc=تقسيم ملفات PDF إلى مستندات متعددة
home.rotate.title=تدوير ملفات PDF
home.rotate.desc=قم بتدوير ملفات PDF الخاصة بك بسهولة.
home.imageToPdf.title=صورة إلى PDF
home.imageToPdf.desc=تحويل الصور (PNG ، JPEG ، GIF) إلى PDF.
home.pdfToImage.title=تحويل PDF إلى صورة
home.pdfToImage.desc=تحويل ملف PDF إلى صورة. (PNG ، JPEG ، GIF)
home.pdfOrganiser.title=منظم PDF
home.pdfOrganiser.desc=إزالة / إعادة ترتيب الصفحات بأي ترتيب
home.addImage.title=إضافة صورة إلى ملف PDF
home.addImage.desc=إضافة صورة إلى موقع معين في PDF (العمل قيد التقدم)
home.watermark.title=إضافة علامة مائية
home.watermark.desc=أضف علامة مائية مخصصة إلى مستند PDF الخاص بك.
home.remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
home.remove-watermark.desc = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF \u0627\u0644\u062E\u0627\u0635 \u0628\u0643.
home.permissions.title=تغيير الأذونات
home.permissions.desc=قم بتغيير أذونات مستند PDF الخاص بك
home.removePages.title=إزالة الصفحات
home.removePages.desc=حذف الصفحات غير المرغوب فيها من مستند PDF الخاص بك.
home.addPassword.title=إضافة كلمة مرور
home.addPassword.desc=تشفير مستند PDF الخاص بك بكلمة مرور.
home.removePassword.title=إزالة كلمة المرور
home.removePassword.desc=إزالة الحماية بكلمة مرور من مستند PDF الخاص بك.
home.compressPdfs.title=ضغط ملفات PDF
home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف.
home.changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
home.changeMetadata.desc = \u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF
home.xlsToPdf.title = \u062A\u062D\u0648\u064A\u0644 Excel (Xls) \u0625\u0644\u0649 PDF
home.xlsToPdf.desc = \u0642\u0645 \u0628\u062A\u062D\u0648\u064A\u0644 \u0645\u0633\u062A\u0646\u062F Excel (xls \u060C xlsx) \u0625\u0644\u0649 PDF.
#Add image
addImage.title=إضافة صورة
addImage.header=إضافة صورة إلى PDF (العمل قيد التقدم)
addImage.submit=إضافة صورة
#compress
compress.title=ضغط
compress.header=ضغط ملف PDF
compress.compressLevel=القيمة بين 1 و 100 (يتم تقليل 1 إلى أقصى حد)
compress.submit=ضغط
#merge
merge.title=دمج
merge.header=دمج ملفات PDF متعددة (2+)
merge.submit=دمج
#pdfOrganiser
pdfOrganiser.title=منظم الصفحة
pdfOrganiser.header=منظم صفحات PDF
pdfOrganiser.submit=إعادة ترتيب الصفحات
#pageRemover
pageRemover.title=مزيل الصفحة
pageRemover.header=مزيل صفحة PDF
pageRemover.pagesToDelete=الصفحات المراد حذفها (أدخل قائمة بأرقام الصفحات مفصولة بفواصل):
pageRemover.submit=حذف الصفحات
#rotate
rotate.title=تدوير PDF
rotate.header=تدوير PDF
rotate.selectAngle=حدد زاوية الدوران (بمضاعفات 90 درجة):
rotate.submit=استدارة
#merge
split.title=انقسام PDF
split.header=تقسيم PDF
split.desc.1=الأرقام التي تحددها هي رقم الصفحة التي تريد تقسيمها
split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-8 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع:
split.desc.3=المستند رقم 1: الصفحة 1
split.desc.4=المستند رقم 2: الصفحتان 2 و 3
split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6
split.desc.6=المستند رقم 4: الصفحة 7
split.desc.7=المستند رقم 5: الصفحة 8
split.desc.8=المستند رقم 6: الصفحتان 9 و 10
split.splitPages=أدخل الصفحات المراد تقسيمها:
split.submit=Split
#merge
imageToPDF.title=صورة إلى PDF
imageToPDF.header=صورة إلى PDF
imageToPDF.submit=تحول
#pdfToImage
pdfToImage.title=تحويل PDF إلى صورة
pdfToImage.header=تحويل PDF إلى صورة
pdfToImage.selectText=تنسيق الصورة
pdfToImage.singleOrMultiple = \u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629
pdfToImage.single = \u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629
pdfToImage.multi = \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629
pdfToImage.colorType = \u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646
pdfToImage.color = \u0627\u0644\u0644\u0648\u0646
pdfToImage.grey = \u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A
pdfToImage.blackwhite = \u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!)
pdfToImage.submit=تحول
#addPassword
addPassword.title=إضافة كلمة مرور
addPassword.header=إضافة كلمة مرور (تشفير)
addPassword.selectText.1=حدد ملف PDF للتشفير
addPassword.selectText.2=كلمة المرور
addPassword.selectText.3=طول مفتاح التشفير
addPassword.selectText.4=القيم الأعلى تكون أقوى ، لكن القيم الأقل لها توافق أفضل.
addPassword.selectText.5=أذونات للتعيين
addPassword.selectText.6=منع تجميع المستند
addPassword.selectText.7=منع استخراج المحتوى
addPassword.selectText.8=منع الاستخراج للوصول
addPassword.selectText.9=منع ملء النموذج
addPassword.selectText.10=منع التعديل
addPassword.selectText.11=منع تعديل التعليقات التوضيحية
addPassword.selectText.12=منع الطباعة
addPassword.selectText.13=منع طباعة تنسيقات مختلفة
addPassword.submit=تشفير
#watermark
watermark.title=إضافة علامة مائية
watermark.header=إضافة علامة مائية
watermark.selectText.1=حدد PDF لإضافة العلامة المائية إلى:
watermark.selectText.2=نص العلامة المائية:
watermark.selectText.3=حجم الخط:
watermark.selectText.4=دوران (0-360):
watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا):
watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا):
watermark.submit=إضافة علامة مائية
#remove-watermark
remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
remove-watermark.header = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
remove-watermark.selectText.1 = \u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646:
remove-watermark.selectText.2 = \u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629:
remove-watermark.submit = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629
#Change permissions
permissions.title=تغيير الأذونات
permissions.header=تغيير الأذونات
permissions.warning=تحذير من أن تكون هذه الأذونات غير قابلة للتغيير ، يوصى بتعيينها بكلمة مرور عبر صفحة إضافة كلمة المرور
permissions.selectText.1=حدد ملف PDF لتغيير الأذونات
permissions.selectText.2=أذونات لتعيينها
permissions.selectText.3=منع تجميع المستند
permissions.selectText.4=منع استخراج المحتوى
permissions.selectText.5=منع الاستخراج للوصول
permissions.selectText.6=منع ملء النموذج
permissions.selectText.7=منع التعديل
permissions.selectText.8=منع تعديل التعليق التوضيحي
permissions.selectText.9=منع الطباعة
permissions.selectText.10=منع طباعة التنسيقات المختلفة
permissions.submit=تغيير
#remove password
removePassword.title=إزالة كلمة المرور
removePassword.header=إزالة كلمة المرور (فك التشفير)
removePassword.selectText.1=حدد PDF لفك التشفير
removePassword.selectText.2=كلمة المرور
removePassword.submit=إزالة
changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
changeMetadata.header = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629
changeMetadata.selectText.1 = \u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627
changeMetadata.selectText.2 = \u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629
changeMetadata.selectText.3 = \u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629:
changeMetadata.author = \u0627\u0644\u0645\u0624\u0644\u0641:
changeMetadata.creationDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss):
changeMetadata.creator = \u0627\u0644\u0645\u0646\u0634\u0626:
changeMetadata.keywords = \u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629:
changeMetadata.modDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss):
changeMetadata.producer = \u0627\u0644\u0645\u0646\u062A\u062C:
changeMetadata.subject = \u0627\u0644\u0645\u0648\u0636\u0648\u0639:
changeMetadata.title = \u0627\u0644\u0639\u0646\u0648\u0627\u0646:
changeMetadata.trapped = \u0645\u062D\u0627\u0635\u0631:
changeMetadata.selectText.4 = \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649:
changeMetadata.selectText.5 = \u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635
changeMetadata.submit = \u062A\u063A\u064A\u064A\u0631
xlsToPdf.title = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
xlsToPdf.header = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF
xlsToPdf.selectText.1 = \u062D\u062F\u062F \u0648\u0631\u0642\u0629 \u0625\u0643\u0633\u0644 XLS \u0623\u0648 XLSX \u0644\u0644\u062A\u062D\u0648\u064A\u0644
xlsToPdf.convert = \u062A\u062D\u0648\u064A\u0644

View File

@@ -0,0 +1,255 @@
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=PDF auswählen
multiPdfPrompt=PDFs auswählen(2+)
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
imgPrompt=Wählen Sie ein Bild
genericSubmit=Einreichen
processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern
pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein):
goToPage=Los
true=Wahr
false=Falsch
unknown=Unbekannt
#############
# HOME-PAGE #
#############
home.desc=Ihr lokal gehosteter One-Stop-Shop für alle Ihre PDF-Anforderungen.
navbar.convert=Konvertieren
navbar.security=Sicherheit
navbar.other=Anderes
navbar.darkmode=Dark Mode
home.merge.title=PDFs zusammenführen
home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen.
home.split.title=PDFs aufteilen
home.split.desc=PDFs in mehrere Dokumente aufteilen.
home.rotate.title=PDFs drehen
home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach.
home.imageToPdf.title=Bild zu PDF
home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF.
home.pdfToImage.title=PDF zu Bild
home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF).
home.pdfOrganiser.title=PDF organisieren
home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern.
home.addImage.title=Bild einfügen
home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (Work in progress).
home.watermark.title=Wasserzeichen hinzufügen
home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu.
home.remove-watermark.title=Wasserzeichen entfernen
home.remove-watermark.desc=Wasserzeichen aus Ihrem PDF-Dokument entfernen.
home.permissions.title=Berechtigungen ändern
home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern.
home.removePages.title=Seiten entfernen
home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen.
home.addPassword.title=Passwort hinzufügen
home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln.
home.removePassword.title=Passwort entfernen
home.removePassword.desc=Den Passwortschutz eines PDFs entfernen.
home.compressPdfs.title=PDF komprimieren
home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren.
home.changeMetadata.title=Metadaten ändern
home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument
home.xlsToPdf.title=Excel (Xls) in PDF
home.xlsToPdf.desc=Konvertiere ein Excel-Dokument (xls, xlsx) in PDF.
#Add image
addImage.title=Bild hinzufügen
addImage.header=Ein Bild einfügen (Work in progress)
addImage.submit=Bild hinzufügen
#compress
compress.title=Komprimieren
compress.header=PDF komprimieren
compress.compressLevel=Wert zwischen 1 und 100 (1 ist am meisten komprimiert)
compress.submit=Komprimieren
#merge
merge.title=Zusammenführen
merge.header=Mehrere PDFs zusammenführen (2+)
merge.submit=Zusammenführen
#pdfOrganiser
pdfOrganiser.title=Seiten anordnen
pdfOrganiser.header=PDF Seitenorganisation
pdfOrganiser.submit=Seiten anordnen
#pageRemover
pageRemover.title=Seiten entfernen
pageRemover.header=PDF Seiten entfernen
pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an):
pageRemover.submit=Seiten löschen
#rotate
rotate.title=PDF drehen
rotate.header=PDF drehen
rotate.selectAngle=Wählen Sie den Winkel (in Vielfachen von 90 Grad):
rotate.submit=Drehen
#merge
split.title=PDF aufteilen
split.header=PDF aufteilen
split.desc.1=Die Nummern, die Sie auswählen, sind die Seitenzahlen, an denen Sie aufteilen möchten.
split.desc.2=So würde die Auswahl von 1,3,7-8 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit:
split.desc.3=Dokument #1: Seite 1
split.desc.4=Dokument #2: Seite 2 und 3
split.desc.5=Dokument #3: Seite 4, 5 und 6
split.desc.6=Dokument #4: Seite 7
split.desc.7=Dokument #5: Seite 8
split.desc.8=Dokument #6: Seite 9 und 10
split.splitPages=Geben Sie die Seiten an, an denen aufgeteilt werden soll:
split.submit=Aufteilen
#merge
imageToPDF.title=Bild zu PDF
imageToPDF.header=Bild zu PDF
imageToPDF.submit=Umwandeln
#pdfToImage
pdfToImage.title=PDF zu Bild
pdfToImage.header=PDF zu Bild
pdfToImage.selectText=Bildformat
pdfToImage.singleOrMultiple=Bildergebnistyp
pdfToImage.single=Einzelnes großes Bild
pdfToImage.multi=Mehrere Bilder
pdfToImage.colorType=Farbtyp
pdfToImage.color=Farbe
pdfToImage.grey=Graustufen
pdfToImage.blackwhite=Schwarzweiß (Datenverlust möglich!)
pdfToImage.submit=Umwandeln
#addPassword
addPassword.title=Passwort hinzufügen
addPassword.header=Passwort hinzufügen (Verschlüsseln)
addPassword.selectText.1=Das zu verschlüsselnde PDF auswählen
addPassword.selectText.2=Passwort
addPassword.selectText.3=Länge des Schlüssels
addPassword.selectText.4=Größere Werte sind stärker, aber niedrigere Werte sind besser kompatibel.
addPassword.selectText.5=Zu setzende Berechtigungen
addPassword.selectText.6=Das zusammensetzen des PDFs verhindern
addPassword.selectText.7=Inhaltsextrahierung verhindern
addPassword.selectText.8=Inhaltsextrahierung zur Barrierefreiheit verhindern
addPassword.selectText.9=Ausfüllen des Formulars verhindern
addPassword.selectText.10=Modifizierung verhindern
addPassword.selectText.11=Ändern von Kommentaren verhindern
addPassword.selectText.12=Drucken verhindern
addPassword.selectText.13=Drucken verschiedener Formate verhindern
addPassword.submit=Verschlüsseln
#watermark
watermark.title=Wasserzeichen hinzufügen
watermark.header=Wasserzeichen hinzufügen
watermark.selectText.1=PDF auswählen, dem ein Wasserzeichen hinzugefügt werden soll:
watermark.selectText.2=Wasserzeichen Text:
watermark.selectText.3=Schriftgröße:
watermark.selectText.4=Drehung (0-360):
watermark.selectText.5=breiteSpacer (horizontaler Abstand zwischen den einzelnen Wasserzeichen):
watermark.selectText.6=höheSpacer (vertikaler Abstand zwischen den einzelnen Wasserzeichen):
watermark.submit=Wasserzeichen hinzufügen
#remove-watermark
remove-watermark.title=Wasserzeichen entfernen
remove-watermark.header=Wasserzeichen entfernen
remove-watermark.selectText.1=PDF auswählen, um Wasserzeichen zu entfernen von:
remove-watermark.selectText.2=Wasserzeichentext:
remove-watermark.submit=Wasserzeichen entfernen
#Change permissions
permissions.title=Berechtigungen ändern
permissions.header=Berechtigungen ändern
permissions.warning=Achtung: Damit diese Berechtigungen nicht geändert werden können, wird empfohlen, sie über die "Passwort hinzufügen"-Seite mit einem Passwort zu versehen
permissions.selectText.1=Das zu ändernde PDF auswählen
permissions.selectText.2=Zu setzende Berechtigungen
permissions.selectText.3=Das zusammensetzen des PDFs verhindern
permissions.selectText.4=Inhaltsextrahierung verhindern
permissions.selectText.5=Inhaltsextrahierung zur Barrierefreiheit verhindern
permissions.selectText.6=Ausfüllen des Formulars verhindern
permissions.selectText.7=Modifizierung verhindern
permissions.selectText.8=Ändern von Kommentaren verhindern
permissions.selectText.9=Drucken verhindern
permissions.selectText.10=Drucken verschiedener Formate verhindern
permissions.submit=Ändern
#remove password
removePassword.title=Passwort entfernen
removePassword.header=Passwort entfernen (Entschlüsseln)
removePassword.selectText.1=Das zu entschlüsselnde PDF auswählen
removePassword.selectText.2=Passwort
removePassword.submit=Entfernen
changeMetadata.title=Metadaten ändern
changeMetadata.header=Metadaten ändern
changeMetadata.selectText.1=Bitte bearbeiten Sie die Variablen, die Sie ändern möchten
changeMetadata.selectText.2=Alle Metadaten löschen
changeMetadata.selectText.3=Benutzerdefinierte Metadaten anzeigen:
changeMetadata.author=Autor:
changeMetadata.creationDate=Erstellungsdatum (jjjj/MM/tt HH:mm:ss):
changeMetadata.creator=Ersteller:
changeMetadata.keywords=Schlüsselwörter:
changeMetadata.modDate=Änderungsdatum (JJJJ/MM/TT HH:mm:ss):
changeMetadata.producer=Produzent:
changeMetadata.subject=Betreff:
changeMetadata.title=Titel:
changeMetadata.trapped=Gefangen:
changeMetadata.selectText.4=Andere Metadaten:
changeMetadata.selectText.5=Benutzerdefinierten Metadateneintrag hinzufügen
changeMetadata.submit=Ändern
xlsToPdf.title=Excel in PDF
xlsToPdf.header=Excel in PDF
xlsToPdf.selectText.1=XLS- oder XLSX-Excel-Tabelle zum Konvertieren auswählen
xlsToPdf.convert=konvertieren

View File

@@ -0,0 +1,252 @@
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=Choose PDF
multiPdfPrompt=Choose PDFs (2+)
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
imgPrompt=Choose Image
genericSubmit=Submit
processTimeWarning=Warning: This process can take up to a minute depending on file-size
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
goToPage=Go
true=True
false=False
unknown=Unknown
#############
# HOME-PAGE #
#############
home.desc=Your locally hosted one-stop-shop for all your PDF needs.
navbar.convert=Convert
navbar.security=Security
navbar.other=Other
navbar.darkmode=Dark Mode
home.merge.title=Merge PDFs
home.merge.desc=Easily merge multiple PDFs into one.
home.split.title=Split PDFs
home.split.desc=Split PDFs into multiple documents
home.rotate.title=Rotate PDFs
home.rotate.desc=Easily rotate your PDFs.
home.imageToPdf.title=Image to PDF
home.imageToPdf.desc=Convert a image (PNG, JPEG, GIF) to PDF.
home.pdfToImage.title=PDF to Image
home.pdfToImage.desc=Convert a PDF to a image. (PNG, JPEG, GIF)
home.pdfOrganiser.title=PDF Organiser
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
home.addImage.title=Add image onto PDF
home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress)
home.watermark.title=Add Watermark
home.watermark.desc=Add a custom watermark to your PDF document.
home.remove-watermark.title=Remove Watermark
home.remove-watermark.desc=Remove watermarks from your PDF document.
home.permissions.title=Change Permissions
home.permissions.desc=Change the permissions of your PDF document
home.removePages.title=Remove Pages
home.removePages.desc=Delete unwanted pages from your PDF document.
home.addPassword.title=Add Password
home.addPassword.desc=Encrypt your PDF document with a password.
home.removePassword.title=Remove Password
home.removePassword.desc=Remove password protection from your PDF document.
home.compressPdfs.title=Compress PDFs
home.compressPdfs.desc=Compress PDFs to reduce their file size.
home.changeMetadata.title=Change Metadata
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
home.xlsToPdf.title=Excel (Xls) to PDF
home.xlsToPdf.desc=Convert a Excel document (xls, xlsx) to PDF.
#Add image
addImage.title=Add Image
addImage.header=Add image to PDF (Work in progress)
addImage.submit=Add image
#compress
compress.title=Compress
compress.header=Compress PDF
compress.compressLevel=Value between 1 and 100 (1 being most reduced)
compress.submit=Compress
#merge
merge.title=Merge
merge.header=Merge multiple PDFs (2+)
merge.submit=Merge
#pdfOrganiser
pdfOrganiser.title=Page Organiser
pdfOrganiser.header=PDF Page Organiser
pdfOrganiser.submit=Rearrange Pages
#pageRemover
pageRemover.title=Page Remover
pageRemover.header=PDF Page remover
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
pageRemover.submit=Delete Pages
#rotate
rotate.title=Rotate PDF
rotate.header=Rotate PDF
rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
rotate.submit=Rotate
#merge
split.title=Split PDF
split.header=Split PDF
split.desc.1=The numbers you select are the page number you wish to do a split on
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
split.desc.3=Document #1: Page 1
split.desc.4=Document #2: Page 2 and 3
split.desc.5=Document #3: Page 4, 5 and 6
split.desc.6=Document #4: Page 7
split.desc.7=Document #5: Page 8
split.desc.8=Document #6: Page 9 and 10
split.splitPages=Enter pages to split on:
split.submit=Split
#merge
imageToPDF.title=Image to PDF
imageToPDF.header=Image to PDF
imageToPDF.submit=Convert
#pdfToImage
pdfToImage.title=PDF to Image
pdfToImage.header=PDF to Image
pdfToImage.selectText=Image Format
pdfToImage.singleOrMultiple=Image result type
pdfToImage.single=Single Big Image
pdfToImage.multi=Multiple Images
pdfToImage.colorType=Colour type
pdfToImage.color=Colour
pdfToImage.grey=Greyscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
#addPassword
addPassword.title=Add Password
addPassword.header=Add password (Encrypt)
addPassword.selectText.1=Select PDF to encrypt
addPassword.selectText.2=Password
addPassword.selectText.3=Encryption Key Length
addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
addPassword.selectText.5=Permissions to set
addPassword.selectText.6=Prevent assembly of document
addPassword.selectText.7=Prevent content extraction
addPassword.selectText.8=Prevent extraction for accessibility
addPassword.selectText.9=Prevent filling in form
addPassword.selectText.10=Prevent modification
addPassword.selectText.11=Prevent annotation modification
addPassword.selectText.12=Prevent printing
addPassword.selectText.13=Prevent printing different formats
addPassword.submit=Encrypt
#watermark
watermark.title=Add Watermark
watermark.header=Add Watermark
watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.submit=Add Watermark
#remove-watermark
remove-watermark.title=Remove Watermark
remove-watermark.header=Remove Watermark
remove-watermark.selectText.1=Select PDF to remove watermark from:
remove-watermark.selectText.2=Watermark Text:
remove-watermark.submit=Remove Watermark
#Change permissions
permissions.title=Change Permissions
permissions.header=Change Permissions
permissions.warning=Warning to have these permissions be unchangeable it is recommended to set them with a password via the add-password page
permissions.selectText.1=Select PDF to change permissions
permissions.selectText.2=Permissions to set
permissions.selectText.3=Prevent assembly of document
permissions.selectText.4=Prevent content extraction
permissions.selectText.5=Prevent extraction for accessibility
permissions.selectText.6=Prevent filling in form
permissions.selectText.7=Prevent modification
permissions.selectText.8=Prevent annotation modification
permissions.selectText.9=Prevent printing
permissions.selectText.10=Prevent printing different formats
permissions.submit=Change
#remove password
removePassword.title=Remove password
removePassword.header=Remove password (Decrypt)
removePassword.selectText.1=Select PDF to Decrypt
removePassword.selectText.2=Password
removePassword.submit=Remove
changeMetadata.title=Change Metadata
changeMetadata.header=Change Metadata
changeMetadata.selectText.1=Please edit the variables you wish to change
changeMetadata.selectText.2=Delete all metadata
changeMetadata.selectText.3=Show Custom Metadata:
changeMetadata.author=Author:
changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
changeMetadata.creator=Creator:
changeMetadata.keywords=Keywords:
changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=Producer:
changeMetadata.subject=Subject:
changeMetadata.title=Title:
changeMetadata.trapped=Trapped:
changeMetadata.selectText.4=Other Metadata:
changeMetadata.selectText.5=Add Custom Metadata Entry
changeMetadata.submit=Change
xlsToPdf.title=Excel to PDF
xlsToPdf.header=Excel to PDF
xlsToPdf.selectText.1=Select XLS or XLSX Excel sheet to convert
xlsToPdf.convert=convert

View File

@@ -0,0 +1,254 @@
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=Choose PDF
multiPdfPrompt=Choose PDFs (2+)
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
imgPrompt=Choose Image
genericSubmit=Submit
processTimeWarning=Warning: This process can take up to a minute depending on file-size
pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) :
goToPage=Go
true=True
false=False
unknown=Unknown
#############
# HOME-PAGE #
#############
home.desc=Your locally hosted one-stop-shop for all your PDF needs.
navbar.convert=Convert
navbar.security=Security
navbar.other=Other
navbar.darkmode=Dark Mode
home.merge.title=Merge PDFs
home.merge.desc=Easily merge multiple PDFs into one.
home.split.title=Split PDFs
home.split.desc=Split PDFs into multiple documents
home.rotate.title=Rotate PDFs
home.rotate.desc=Easily rotate your PDFs.
home.imageToPdf.title=Image to PDF
home.imageToPdf.desc=Convert a images (PNG, JPEG, GIF) to PDF.
home.pdfToImage.title=PDF to Image
home.pdfToImage.desc=Convert a PDF to a image. (PNG, JPEG, GIF)
home.pdfOrganiser.title=PDF Organizer
home.pdfOrganiser.desc=Remove/Rearrange pages in any order
home.addImage.title=Add image onto PDF
home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress)
home.watermark.title=Add Watermark
home.watermark.desc=Add a custom watermark to your PDF document.
home.remove-watermark.title=Remove Watermark
home.remove-watermark.desc=Remove watermarks from your PDF document.
home.permissions.title=Change Permissions
home.permissions.desc=Change the permissions of your PDF document
home.removePages.title=Remove Pages
home.removePages.desc=Delete unwanted pages from your PDF document.
home.addPassword.title=Add Password
home.addPassword.desc=Encrypt your PDF document with a password.
home.removePassword.title=Remove Password
home.removePassword.desc=Remove password protection from your PDF document.
home.compressPdfs.title=Compress PDFs
home.compressPdfs.desc=Compress PDFs to reduce their file size.
home.changeMetadata.title=Change Metadata
home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document
home.xlsToPdf.title=Excel (Xls) to PDF
home.xlsToPdf.desc=Convert a Excel document (xls, xlsx) to PDF.
#Add image
addImage.title=Add Image
addImage.header=Add image to PDF (Work in progress)
addImage.submit=Add image
#compress
compress.title=Compress
compress.header=Compress PDF
compress.compressLevel=Value between 1 and 100 (1 being most reduced)
compress.submit=Compress
#merge
merge.title=Merge
merge.header=Merge multiple PDFs (2+)
merge.submit=Merge
#pdfOrganiser
pdfOrganiser.title=Page Organizer
pdfOrganiser.header=PDF Page Organizer
pdfOrganiser.submit=Rearrange Pages
#pageRemover
pageRemover.title=Page Remover
pageRemover.header=PDF Page remover
pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) :
pageRemover.submit=Delete Pages
#rotate
rotate.title=Rotate PDF
rotate.header=Rotate PDF
rotate.selectAngle=Select rotation angle (in multiples of 90 degrees):
rotate.submit=Rotate
#merge
split.title=Split PDF
split.header=Split PDF
split.desc.1=The numbers you select are the page number you wish to do a split on
split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with:
split.desc.3=Document #1: Page 1
split.desc.4=Document #2: Page 2 and 3
split.desc.5=Document #3: Page 4, 5 and 6
split.desc.6=Document #4: Page 7
split.desc.7=Document #5: Page 8
split.desc.8=Document #6: Page 9 and 10
split.splitPages=Enter pages to split on:
split.submit=Split
#merge
imageToPDF.title=Image to PDF
imageToPDF.header=Image to PDF
imageToPDF.submit=Convert
#pdfToImage
pdfToImage.title=PDF to Image
pdfToImage.header=PDF to Image
pdfToImage.selectText=Image Format
pdfToImage.singleOrMultiple=Image result type
pdfToImage.single=Single Big Image
pdfToImage.multi=Multiple Images
pdfToImage.colorType=Color type
pdfToImage.color=Color
pdfToImage.grey=Grayscale
pdfToImage.blackwhite=Black and White (May lose data!)
pdfToImage.submit=Convert
#addPassword
addPassword.title=Add Password
addPassword.header=Add password (Encrypt)
addPassword.selectText.1=Select PDF to encrypt
addPassword.selectText.2=Password
addPassword.selectText.3=Encryption Key Length
addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
addPassword.selectText.5=Permissions to set
addPassword.selectText.6=Prevent assembly of document
addPassword.selectText.7=Prevent content extraction
addPassword.selectText.8=Prevent extraction for accessibility
addPassword.selectText.9=Prevent filling in form
addPassword.selectText.10=Prevent modification
addPassword.selectText.11=Prevent annotation modification
addPassword.selectText.12=Prevent printing
addPassword.selectText.13=Prevent printing different formats
addPassword.submit=Encrypt
#watermark
watermark.title=Add Watermark
watermark.header=Add Watermark
watermark.selectText.1=Select PDF to add watermark to:
watermark.selectText.2=Watermark Text:
watermark.selectText.3=Font Size:
watermark.selectText.4=Rotation (0-360):
watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
watermark.selectText.6=heightSpacer (Space between each watermark vertically):
watermark.submit=Add Watermark
#remove-watermark
remove-watermark.title=Remove Watermark
remove-watermark.header=Remove Watermark
remove-watermark.selectText.1=Select PDF to remove watermark from:
remove-watermark.selectText.2=Watermark Text:
remove-watermark.submit=Remove Watermark
#Change permissions
permissions.title=Change Permissions
permissions.header=Change Permissions
permissions.warning=Warning to have these permissions be unchangeable it is recommended to set them with a password via the add-password page
permissions.selectText.1=Select PDF to change permissions
permissions.selectText.2=Permissions to set
permissions.selectText.3=Prevent assembly of document
permissions.selectText.4=Prevent content extraction
permissions.selectText.5=Prevent extraction for accessibility
permissions.selectText.6=Prevent filling in form
permissions.selectText.7=Prevent modification
permissions.selectText.8=Prevent annotation modification
permissions.selectText.9=Prevent printing
permissions.selectText.10=Prevent printing different formats
permissions.submit=Change
#remove password
removePassword.title=Remove password
removePassword.header=Remove password (Decrypt)
removePassword.selectText.1=Select PDF to Decrypt
removePassword.selectText.2=Password
removePassword.submit=Remove
changeMetadata.title=Change Metadata
changeMetadata.header=Change Metadata
changeMetadata.selectText.1=Please edit the variables you wish to change
changeMetadata.selectText.2=Delete all metadata
changeMetadata.selectText.3=Show Custom Metadata:
changeMetadata.author=Author:
changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
changeMetadata.creator=Creator:
changeMetadata.keywords=Keywords:
changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
changeMetadata.producer=Producer:
changeMetadata.subject=Subject:
changeMetadata.title=Title:
changeMetadata.trapped=Trapped:
changeMetadata.selectText.4=Other Metadata:
changeMetadata.selectText.5=Add Custom Metadata Entry
changeMetadata.submit=Change
xlsToPdf.title=Excel to PDF
xlsToPdf.header=Excel to PDF
xlsToPdf.selectText.1=Select XLS or XLSX Excel sheet to convert
xlsToPdf.convert=convert

View File

@@ -0,0 +1,235 @@
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!#
# Translated by Google Translate #
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!#
###########
# Generic #
###########
# the direction that the language is written (ltr = left to right, rtl = right to left)
language.direction=ltr
pdfPrompt=Choisir PDF
multiPdfPrompt=Choisir des PDF (2+)
multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin
imgPrompt=Choisir une image
genericSubmit=Soumettre
processTimeWarning=AttentionÂ: ce processus peut prendre jusqu'à une minute en fonction de la taille du fichier
pageOrderPrompt=Ordre des pages (Entrez une liste de numéros de page séparés par des virgules)Â:
goToPage=Aller
true=Vrai
false=Faux
unknown=Inconnu
#############
# HOME-PAGE #
#############
home.desc=Votre guichet unique hébergé localement pour tous vos besoins PDF.
navbar.convert=Convertir
navbar.security=Sécurité
navbar.other=Autre
navbar.darkmode=Mode sombre
home.merge.title=Fusionner des PDF
home.merge.desc=Fusionnez facilement plusieurs PDF en un seul.
home.split.title=Fractionner les PDF
home.split.desc=Diviser les PDF en plusieurs documents
home.rotate.title=Faire pivoter les PDF
home.rotate.desc=Faites pivoter facilement vos PDF.
home.imageToPdf.title=Image au format PDF
home.imageToPdf.desc=Convertir une image (PNG, JPEG, GIF) en PDF.
home.pdfToImage.title=PDF vers image
home.pdfToImage.desc=Convertir un PDF en image. (PNG, JPEG, GIF)
home.pdfOrganiser.title=Organisateur PDF
home.pdfOrganiser.desc=Supprimer/Réorganiser les pages dans n'importe quel ordre
home.addImage.title=Ajouter une image au PDF
home.addImage.desc=Ajoute une image à un emplacement défini sur le PDF (Travail en cours)
home.watermark.title=Ajouter un filigrane
home.watermark.desc=Ajoutez un filigrane personnalisé à votre document PDF.
home.remove-watermark.title=Supprimer le filigrane
home.remove-watermark.desc=Supprimez les filigranes de votre document PDF.
home.permissions.title=Modifier les autorisations
home.permissions.desc=Modifier les permissions de votre document PDF
home.removePages.title=Supprimer des pages
home.removePages.desc=Supprimez les pages inutiles de votre document PDF.
home.addPassword.title=Ajouter un mot de passe
home.addPassword.desc=Cryptez votre document PDF avec un mot de passe.
home.removePassword.title=Supprimer le mot de passe
home.removePassword.desc=Supprimez la protection par mot de passe de votre document PDF.
home.compressPdfs.title=Compresser les PDF
home.compressPdfs.desc=Compressez les PDF pour réduire leur taille de fichier.
home.changeMetadata.title=Modifier les métadonnées
home.changeMetadata.desc=Modifier/Supprimer/Ajouter des métadonnées d'un document PDF
home.xlsToPdf.title=Excel (Xls) en PDF
home.xlsToPdf.desc=Convertir un document Excel (xls, xlsx) en PDF.
#Add image
addImage.title=Ajouter une image
addImage.header=Ajouter une image au PDF (Travail en cours)
addImage.submit=Ajouter une image
#compress
compress.title=Compresser
compress.header=Compresser le PDF
compress.compressLevel=Valeur entre 1 et 100 (1 étant le plus réduit)
compress.submit=Compresser
#merge
merge.title=Fusionner
merge.header=Fusionner plusieurs PDF (2+)
merge.submit=Fusionner
#pdfOrganiser
pdfOrganiser.title=Organisateur de pages
pdfOrganiser.header=Organisateur de pages PDF
pdfOrganiser.submit=Réorganiser les pages
#pageRemover
pageRemover.title=Suppresseur de pages
pageRemover.header=Outil de suppression de pages PDF
pageRemover.pagesToDelete=Pages à supprimer (Entrez une liste de numéros de page séparés par des virgules)Â:
pageRemover.submit=Supprimer des pages
#rotate
rotate.title=Faire pivoter le PDF
rotate.header=Faire pivoter le PDF
rotate.selectAngle=S\u00e9lectionner l'angle de rotation (en multiples de 90 degr\u00e9s):
rotate.submit=Rotation
#merge
split.title=Fractionner le PDF
split.header=Diviser le PDF
split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire un fractionnement
split.desc.2=Ainsi, la sélection de 1,3,7-8 diviserait un document de 10 pages en 6 PDF distincts avecÂ:
split.desc.3=Document #1Â: Page 1
split.desc.4=Document #2Â: Pages 2 et 3
split.desc.5=Document #3Â: Pages 4, 5 et 6
split.desc.6=Document #4Â: Page 7
split.desc.7=Document #5Â: Page 8
split.desc.8=Document #6Â: Pages 9 et 10
split.splitPages=Entrez les pages sur lesquelles fractionnerÂ:
split.submit=Diviser
#merge
imageToPDF.title=Image au format PDF
imageToPDF.header=Image au format PDF
imageToPDF.submit=Convertir
#pdfToImage
pdfToImage.title=PDF vers image
pdfToImage.header=PDF vers image
pdfToImage.selectText=Format d'image
pdfToImage.singleOrMultiple=Type de résultat d'image
pdfToImage.single=Une seule grande image
pdfToImage.multi=Plusieurs images
pdfToImage.colorType=Type de couleur
pdfToImage.color=Couleur
pdfToImage.grey=Niveaux de gris
pdfToImage.blackwhite=Noir et Blanc (Peut perdre des données !)
pdfToImage.submit=Convertir
#addPassword
addPassword.title=Ajouter un mot de passe
addPassword.header=Ajouter un mot de passe (chiffrer)
addPassword.selectText.1=Sélectionnez le PDF à chiffrer
addPassword.selectText.2=Mot de passe
addPassword.selectText.3=Longueur de la clé de chiffrement
addPassword.selectText.4=Les valeurs supérieures sont plus fortes, mais les valeurs inférieures ont une meilleure compatibilité.
addPassword.selectText.5=Autorisations à définir
addPassword.selectText.6=Empêcher l'assemblage du document
addPassword.selectText.7=Empêcher l'extraction de contenu
addPassword.selectText.8=Empêcher l'extraction pour l'accessibilité
addPassword.selectText.9=Empêcher de remplir le formulaire
addPassword.selectText.10=Empêcher la modification
addPassword.selectText.11=Empêcher la modification des annotations
addPassword.selectText.12=Empêcher l'impression
addPassword.selectText.13=Empêcher l'impression de différents formats
addPassword.submit=Crypter
#watermark
watermark.title=Ajouter un filigrane
watermark.header=Ajouter un filigrane
watermark.selectText.1=Sélectionnez le PDF auquel ajouter un filigraneÂ:
watermark.selectText.2=Texte du filigraneÂ:
watermark.selectText.3=Taille de la policeÂ:
watermark.selectText.4=Rotation (0-360)Â:
watermark.selectText.5=widthSpacer (Espace entre chaque filigrane horizontalement)Â:
watermark.selectText.6=heightSpacer (Espace entre chaque filigrane verticalement)Â:
watermark.submit=Ajouter un filigrane
#remove-watermark
remove-watermark.title=Supprimer le filigrane
remove-watermark.header=Supprimer le filigrane
remove-watermark.selectText.1=Sélectionnez le PDF pour supprimer le filigrane:
remove-watermark.selectText.2=Texte du filigrane:
remove-watermark.submit=Supprimer le filigrane
#Change permissions
permissions.title=Modifier les autorisations
permissions.header=Modifier les autorisations
permissions.warning=Attention pour que ces permissions soient immuables il est recommandé de les définir avec un mot de passe via la page add-password
permissions.selectText.1=Sélectionnez PDF pour modifier les autorisations
permissions.selectText.2=Autorisations à définir
permissions.selectText.3=Employer l'assemblage du document
permissions.selectText.4=Employer l'extraction de contenu
permissions.selectText.5=Employer l'extraction pour l'accessibilité
permissions.selectText.6=Employer de remplir le formulaire
permissions.selectText.7=Employer la modification
permissions.selectText.8=Employer la modification des annotations
permissions.selectText.9=Employer l'impression
permissions.selectText.10=Emp�cher l'impression de diff�rents formats
permissions.submit=Modificateur
#supprimer le mot de passe
removePassword.title=Supprimer le mot de passe
removePassword.header=Supprimer le mot de passe (Déchiffrer)
removePassword.selectText.1=Sélectionnez le PDF à déchiffrer
removePassword.selectText.2=Mot de passe
removePassword.submit=Supprimer
changeMetadata.title=Modifier les métadonnées
changeMetadata.header=Modifier les métadonnées
changeMetadata.selectText.1=Veuillez modifier les variables que vous souhaitez modifier
changeMetadata.selectText.2=Supprimer toutes les métadonnées
changeMetadata.selectText.3=Afficher les métadonnées personnalisées:
changeMetadata.author=Auteur:
changeMetadata.creationDate=Date de création (aaaa/MM/jj HH:mm:ss):
changeMetadata.creator=Créateur:
changeMetadata.keywords=Mots clés:
changeMetadata.modDate=Date de modification (aaaa/MM/jj HH:mm:ss):
changeMetadata.producer=Producteur:
changeMetadata.subject=Objet:
changeMetadata.title=Titre:
changeMetadata.trapped=Piégé:
changeMetadata.selectText.4=Autres métadonnées:
changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisées
changeMetadata.submit=Modifier
xlsToPdf.title=Excel vers PDF
xlsToPdf.header=Excel en PDF
xlsToPdf.selectText.1=Sélectionnez une feuille Excel XLS ou XLSX à convertir
xlsToPdf.convert=convertir

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,16 +1,16 @@
/* Dark Mode Styles */
body {
background-color: #333;
color: #fff;
background-color: #333 !important;
color: #fff !important;
}
.dark-card {
background-color: #333 !important;
color: white;
color: white !important;
}
.jumbotron {
background-color: #222; /* or any other dark color */
color: #fff; /* or any other light color */
color: #fff !important; /* or any other light color */
}
.list-group {
@@ -20,4 +20,7 @@ body {
.list-group-item {
background-color: #222 !important;
color: fff !important;
}
#support-section {
background-color: #444 !important;
}

View File

@@ -0,0 +1,23 @@
#page-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
#content-wrap {
flex: 1;
}
#footer {
bottom: 0;
width: 100%;
}
html[lang-direction=ltr] * {
direction: ltr;
}
html[lang-direction=rtl] * {
direction: rtl;
text-align: right;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1 +1,298 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 100 100"><rect width="100" height="100" rx="50" fill="#000000"></rect><path fill="#ffffff" d="M22.76 53.83L18.32 53.83L18.32 63.29Q18.06 63.38 17.62 63.49Q17.18 63.60 16.65 63.60L16.65 63.60Q14.71 63.60 14.71 61.97L14.71 61.97L14.71 38.56Q14.71 37.86 15.06 37.48Q15.42 37.11 16.16 36.89L16.16 36.89Q17.44 36.49 19.27 36.32Q21.09 36.14 22.81 36.14L22.81 36.14Q28.40 36.14 30.97 38.49Q33.54 40.85 33.54 44.85L33.54 44.85Q33.54 48.94 30.90 51.39Q28.26 53.83 22.76 53.83L22.76 53.83ZM18.28 50.84L22.54 50.84Q26.06 50.84 28.00 49.38Q29.94 47.93 29.94 44.90L29.94 44.90Q29.94 41.90 28.07 40.52Q26.20 39.13 22.72 39.13L22.72 39.13Q21.53 39.13 20.37 39.24Q19.20 39.35 18.28 39.53L18.28 39.53L18.28 50.84ZM58.40 49.69L58.40 49.69Q58.40 46.88 57.55 44.87Q56.69 42.87 55.21 41.60Q53.74 40.32 51.78 39.73Q49.82 39.13 47.58 39.13L47.58 39.13Q46.17 39.13 45.14 39.22Q44.10 39.31 43.22 39.48L43.22 39.48L43.22 60.43Q44.28 60.69 45.49 60.78Q46.70 60.87 47.98 60.87L47.98 60.87Q53.17 60.87 55.79 58.05Q58.40 55.24 58.40 49.69ZM62.06 49.69L62.06 49.69Q62.06 53.30 61.07 55.96Q60.08 58.62 58.25 60.38Q56.42 62.14 53.83 63.00Q51.23 63.86 48.02 63.86L48.02 63.86Q46.61 63.86 44.85 63.75Q43.09 63.64 41.51 63.16L41.51 63.16Q39.66 62.58 39.66 61.13L39.66 61.13L39.66 38.52Q39.66 37.81 40.01 37.44Q40.36 37.06 41.11 36.84L41.11 36.84Q42.48 36.40 44.19 36.27Q45.91 36.14 47.62 36.14L47.62 36.14Q50.84 36.14 53.50 37.00Q56.16 37.86 58.05 39.55Q59.94 41.24 61 43.77Q62.06 46.30 62.06 49.69ZM70.28 36.62L84.89 36.62Q85.02 36.84 85.16 37.22Q85.29 37.59 85.29 38.03L85.29 38.03Q85.29 38.78 84.94 39.22Q84.58 39.66 83.92 39.66L83.92 39.66L71.96 39.66L71.96 48.81L83.35 48.81Q83.48 49.03 83.62 49.41Q83.75 49.78 83.75 50.22L83.75 50.22Q83.75 50.97 83.40 51.41Q83.04 51.85 82.38 51.85L82.38 51.85L71.96 51.85L71.96 63.33Q71.74 63.42 71.27 63.51Q70.81 63.60 70.33 63.60L70.33 63.60Q68.35 63.60 68.35 62.01L68.35 62.01L68.35 38.56Q68.35 37.68 68.88 37.15Q69.40 36.62 70.28 36.62L70.28 36.62Z"></path></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="99.537987mm"
height="95.209366mm"
viewBox="0 0 99.537987 95.209366"
version="1.1"
id="svg745"
xml:space="preserve"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="stirling-transparent.svg"
inkscape:export-filename="stirling.png"
inkscape:export-xdpi="80"
inkscape:export-ydpi="80"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview747"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.914906"
inkscape:cx="175.42786"
inkscape:cy="510.43495"
inkscape:window-width="2256"
inkscape:window-height="1428"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
inkscape:current-layer="svg745" /><defs
id="defs742"><linearGradient
inkscape:collect="always"
id="linearGradient72198"><stop
style="stop-color:#ccd6d7;stop-opacity:1;"
offset="0"
id="stop72194" /><stop
style="stop-color:#0f3a3f;stop-opacity:1;"
offset="1"
id="stop72196" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71928"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71924" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71926" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient71920"><stop
style="stop-color:#4b0005;stop-opacity:1;"
offset="0"
id="stop71916" /><stop
style="stop-color:#8f000c;stop-opacity:1;"
offset="1"
id="stop71918" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient69598"><stop
style="stop-color:#6a0007;stop-opacity:1;"
offset="0"
id="stop69594" /><stop
style="stop-color:#b8000f;stop-opacity:1;"
offset="1"
id="stop69596" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient46361"><stop
style="stop-color:#f7f6f8;stop-opacity:1;"
offset="0"
id="stop46359" /><stop
style="stop-color:#b7b7b5;stop-opacity:1;"
offset="1"
id="stop46357" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient40554"><stop
style="stop-color:#f4f2f4;stop-opacity:1;"
offset="0"
id="stop40550" /><stop
style="stop-color:#57767b;stop-opacity:1;"
offset="1"
id="stop40552" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient39095"><stop
style="stop-color:#285459;stop-opacity:1;"
offset="0"
id="stop39093" /><stop
style="stop-color:#a6b6af;stop-opacity:1;"
offset="1"
id="stop39091" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient36672"><stop
style="stop-color:#da453f;stop-opacity:1;"
offset="0"
id="stop36668" /><stop
style="stop-color:#a60008;stop-opacity:1;"
offset="1"
id="stop36670" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient19524"><stop
style="stop-color:#3a4b4f;stop-opacity:1;"
offset="0"
id="stop19522" /><stop
style="stop-color:#617979;stop-opacity:0.97461927;"
offset="1"
id="stop19520" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient17996"><stop
style="stop-color:#401016;stop-opacity:1;"
offset="0"
id="stop17994" /><stop
style="stop-color:#761f19;stop-opacity:1;"
offset="1"
id="stop17992" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15569"><stop
style="stop-color:#8ea8ad;stop-opacity:1;"
offset="0"
id="stop15565" /><stop
style="stop-color:#e9e7eb;stop-opacity:1;"
offset="1"
id="stop15567" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient15557"><stop
style="stop-color:#9b0e11;stop-opacity:1;"
offset="0"
id="stop15553" /><stop
style="stop-color:#370707;stop-opacity:1;"
offset="1"
id="stop15555" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15557"
id="linearGradient15559"
x1="120.06672"
y1="63.25761"
x2="135.16347"
y2="78.078682"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15569"
id="linearGradient15571"
x1="127.97037"
y1="101.66144"
x2="133.88971"
y2="104.77026"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient17996"
id="linearGradient17998"
x1="117.9284"
y1="86.055084"
x2="130.67392"
y2="76.945541"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient19524"
id="linearGradient19528"
x1="130.98172"
y1="82.402977"
x2="135.72115"
y2="86.45166"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient36672"
id="linearGradient36674"
x1="63.797714"
y1="74.81752"
x2="96.636673"
y2="120.29293"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient39095"
id="linearGradient39097"
x1="120.54506"
y1="124.76902"
x2="128.04152"
y2="126.0704"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient40554"
id="linearGradient40556"
x1="113.84585"
y1="114.04285"
x2="119.65858"
y2="128.50244"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient46361"
id="linearGradient46363"
x1="73.993439"
y1="114.13906"
x2="94.845322"
y2="71.247383"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient69598"
id="linearGradient69600"
x1="95.854446"
y1="114.66749"
x2="103.77842"
y2="120.1887"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71920"
id="linearGradient71922"
x1="98.580376"
y1="87.186958"
x2="118.09738"
y2="101.19449"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient71928"
id="linearGradient71930"
x1="78.278267"
y1="97.433273"
x2="92.313202"
y2="104.33479"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient72198"
id="linearGradient72200"
x1="125.76636"
y1="138.46817"
x2="123.3327"
y2="126.03291"
gradientUnits="userSpaceOnUse" /></defs><g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="shadow"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient72200);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.146049,134.73858 c 0,0 11.721038,2.48294 17.938661,2.91673 6.21763,0.43378 14.75251,0.59994 22.41237,-0.43379 8.01008,-1.081 13.19907,-2.22733 14.50043,-2.66112 1.30136,-0.43379 4.00784,-1.24297 4.15244,-2.25514 0.1446,-1.01217 -3.4703,-2.74733 -6.21763,-3.32571 -2.74732,-0.57838 -12.72444,-1.44596 -14.89337,-1.44596 -2.16894,0 -37.892901,7.20499 -37.892901,7.20499 z"
id="path72192"
sodipodi:nodetypes="cssssssc" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Origami"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient40556);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.460552,134.86721 35.165798,-6.85679 16.15467,-42.7383 z"
id="path960"
sodipodi:nodetypes="cccc" /><path
style="fill:url(#linearGradient15571);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.71493,85.428056 0.3984,45.049024 -9.66213,-20.46173 z"
id="path964"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient39097);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.60518,128.00293 16.5337,2.48693 -9.68769,-20.5512 z"
id="path966"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient15559);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 118.42209,57.022622 12.70423,-2.766809 -0.0785,25.087175 -12.43878,-13.376518 z"
id="path968"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient19528);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 135.72114,85.386768 -4.84219,-6.459493 0.0305,11.126604 z"
id="path970"
sodipodi:nodetypes="cccc" /><path
style="display:inline;fill:url(#linearGradient17998);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 119.10273,65.682415 11.96883,13.44935 -0.0899,10.819868 -11.88577,11.430427 z"
id="path972"
sodipodi:nodetypes="ccccc" /><path
style="display:inline;fill:url(#linearGradient36674);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 62.145635,130.15618 62.043392,65.435258 c 0,0 0.179321,-2.778132 1.89516,-4.036097 1.874923,-1.374597 4.341768,-1.894096 4.341768,-1.894096 l 50.91788,-11.349167 -0.0113,53.144272 -34.733274,33.56547 z"
id="path958"
sodipodi:nodetypes="ccsccccc" /></g><g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Letter"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-51.420144,-44.470286)"><path
style="display:inline;fill:url(#linearGradient69600);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 94.780881,91.406803 16.870379,17.074877 -23.723345,23.00249 -18.122131,-17.99816 5.497473,-2.80607 18.404054,-0.0511 2.35163,-8.23071 z"
id="path54894"
sodipodi:nodetypes="cccccccc" /><path
style="display:inline;fill:url(#linearGradient71930);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 72.440405,92.224764 16.15467,15.745686 4.089788,-6.79927 z"
id="path54892" /><path
style="display:inline;fill:url(#linearGradient71922);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 1.124691,-14.109776 22.92453,22.286787 0.008,8.164604 -3.28863,3.16649 z"
id="path54890"
sodipodi:nodetypes="cccccc"
inkscape:label="path54890" /><path
style="display:inline;fill:url(#linearGradient46363);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.138739,84.965385 h 1.226936 l -0.05112,-14.109776 c 0,0 -5.776827,-3.220709 -12.167126,-2.40275 -6.390296,0.817957 -8.151582,2.1248 -10.58233,4.396523 -1.90229,1.777838 -2.913974,3.527446 -3.987546,7.157132 -0.512646,1.733226 -0.281963,5.988892 0.613471,8.537436 0.664591,1.891528 2.453873,4.294281 4.958868,6.134686 2.662335,1.956002 8.281825,3.527443 8.281825,3.527443 0,0 5.134614,1.887351 5.572338,4.294281 0.308137,1.69437 -0.102243,3.22071 -1.635914,4.95887 -1.258314,1.42609 -3.62969,1.99377 -6.288054,1.07357 -2.658364,-0.92021 -6.139514,-3.85065 -7.106009,-4.90775 -0.817958,-0.89464 -2.820665,-3.06173 -2.890231,-4.294021 -0.01209,-0.214205 -1.229505,-0.0963 -1.229505,-0.0963 l -0.0723,14.256941 5.879073,2.24938 c 0,0 5.214483,1.78929 8.946415,1.43143 3.731934,-0.35786 7.617235,-0.51122 11.604778,-5.16336 3.987542,-4.65213 3.680812,-12.831715 2.147141,-15.899056 -1.533673,-3.067344 -3.561212,-6.138812 -10.480087,-8.281826 -5.776829,-1.789283 -7.872846,-3.01622 -8.128458,-4.396524 -0.255611,-1.380305 0.0091,-4.253646 2.760607,-5.214481 3.220711,-1.124693 5.623462,-0.05112 7.05489,1.12469 1.431425,1.175817 5.572339,5.623462 5.572339,5.623462 z"
id="path46355"
sodipodi:nodetypes="cccssssscssssscccssssssscc" /></g></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,14 +0,0 @@
#page-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
#content-wrap {
flex: 1;
}
#footer {
bottom: 0;
width: 100%;
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="50px" height="50px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<path stroke="#007bff" stroke-width="38" d="M 297.507 242.806 L 339.507 242.806 M 247.507 242.806 L 289.507 242.806 M 198.507 242.806 L 240.507 242.806 M 149.507 242.806 L 190.507 242.806 M 99.507 242.806 L 141.507 242.806 M 149.507 196.806 L 190.507 196.806 M 198.507 196.806 L 240.507 196.806 M 247.507 196.806 L 289.507 196.806 M 247.507 150.806 L 289.507 150.806"/>
<path fill="#007bff" d="M 473.507 244.806 C 473.507 244.806 455.507 227.806 418.507 233.806 C 414.507 204.806 383.507 187.806 383.507 187.806 C 383.507 187.806 354.507 222.806 375.507 261.806 C 369.507 264.806 359.507 268.806 344.507 268.806 L 69.507 268.806 C 64.507 287.806 64.507 413.806 202.507 413.806 C 301.507 413.806 375.507 367.806 410.507 283.806 C 462.507 287.806 473.507 244.806 473.507 244.806"/>
</svg>

After

Width:  |  Height:  |  Size: 919 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="35px" height="35px" viewBox="0 -0.5 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Github-color</title>
<desc>Created with Sketch.</desc>
<defs>
</defs>
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Color-" transform="translate(-700.000000, -560.000000)" fill="#007bff">
<path d="M723.9985,560 C710.746,560 700,570.787092 700,584.096644 C700,594.740671 706.876,603.77183 716.4145,606.958412 C717.6145,607.179786 718.0525,606.435849 718.0525,605.797328 C718.0525,605.225068 718.0315,603.710086 718.0195,601.699648 C711.343,603.155898 709.9345,598.469394 709.9345,598.469394 C708.844,595.686405 707.2705,594.94548 707.2705,594.94548 C705.091,593.450075 707.4355,593.480194 707.4355,593.480194 C709.843,593.650366 711.1105,595.963499 711.1105,595.963499 C713.2525,599.645538 716.728,598.58234 718.096,597.964902 C718.3135,596.407754 718.9345,595.346062 719.62,594.743683 C714.2905,594.135281 708.688,592.069123 708.688,582.836167 C708.688,580.205279 709.6225,578.054788 711.1585,576.369634 C710.911,575.759726 710.0875,573.311058 711.3925,569.993458 C711.3925,569.993458 713.4085,569.345902 717.9925,572.46321 C719.908,571.928599 721.96,571.662047 724.0015,571.651505 C726.04,571.662047 728.0935,571.928599 730.0105,572.46321 C734.5915,569.345902 736.603,569.993458 736.603,569.993458 C737.9125,573.311058 737.089,575.759726 736.8415,576.369634 C738.3805,578.054788 739.309,580.205279 739.309,582.836167 C739.309,592.091712 733.6975,594.129257 728.3515,594.725612 C729.2125,595.469549 729.9805,596.939353 729.9805,599.18773 C729.9805,602.408949 729.9505,605.006706 729.9505,605.797328 C729.9505,606.441873 730.3825,607.191834 731.6005,606.9554 C741.13,603.762794 748,594.737659 748,584.096644 C748,570.787092 737.254,560 723.9985,560" id="Github">
</path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,54 +1,40 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Add-Image')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{addImage.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Add image to PDF (Work in progress)</h2>
<form method="post" th:action="@{add-image}"
enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput2"
name="fileInput2" required> <label
class="custom-file-label" for="fileInput2">Choose Image</label>
</div>
<div class="form-group">
<label for="x">X</label> <input type="number" class="form-control"
id="x" name="x" step="0.01" required>
</div>
<div class="form-group">
<label for="y">Y</label> <input type="number" class="form-control"
id="y" name="y" step="0.01" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{addImage.header}"></h2>
<form method="post" th:action="@{add-image}" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput2" name="fileInput2" required>
<label class="custom-file-label" for="fileInput2" th:text="#{imgPrompt}"></label>
</div>
<div class="form-group">
<label for="x">X</label> <input type="number" class="form-control" id="x" name="x" step="0.01" required>
</div>
<div class="form-group">
<label for="y">Y</label> <input type="number" class="form-control" id="y" name="y" step="0.01" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{addImage.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,46 +1,36 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Compress')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{compress.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Compress PDF</h2>
<form action="#" th:action="@{compress-pdf}"
th:object="${rotateForm}" method="post"
enctype="multipart/form-data">
<p>Warning: This process can take up to a minute depending on
file-size</p>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label for="imageCompressionLevel">Value between 1 and 100
(1 being most reduced)</label> <input type="number" class="form-control"
id="imageCompressionLevel" name="imageCompressionLevel" step="1"
value="1" min="1" max="100" required>
</div>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{compress.header}"></h2>
<form action="#" th:action="@{compress-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
<p th:text="#{processTimeWarning}"></p>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label for="imageCompressionLevel" th:text="#{compress.compressLevel}"></label>
<input type="number" class="form-control" id="imageCompressionLevel" name="imageCompressionLevel" step="1" value="1" min="1" max="100" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{compress.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
<button type="submit" class="btn btn-primary">Compress</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,38 +1,35 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Image to PDF')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{imageToPDF.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Image to PDF</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{imageToPDF.header}"></h2>
<form method="post" enctype="multipart/form-data"
th:action="@{img-to-pdf}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose Image</label>
</div>
<br>
<br>
<button type="submit" class="btn btn-primary">Convert</button>
<form method="post" enctype="multipart/form-data" th:action="@{img-to-pdf}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" required>
<label class="custom-file-label" for="fileInput" th:text="#{imgPrompt}"></label>
</div>
<br> <br>
<button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,43 +1,57 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='PDF to Image')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{pdfToImage.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>PDF to Image</h2>
<form method="post" enctype="multipart/form-data"
th:action="@{pdf-to-img}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label>Image Format</label> <select class="form-control"
name="imageFormat">
<option value="jpg">JPEG</option>
<option value="png">PNG</option>
<option value="gif">GIF</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Convert</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{pdfToImage.header}"></h2>
<p th:text="#{processTimeWarning}"></p>
<form method="post" enctype="multipart/form-data" th:action="@{pdf-to-img}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label th:text="#{pdfToImage.selectText}"></label>
<select class="form-control" name="imageFormat">
<option value="png">PNG</option>
</select>
</div>
<div class="form-group">
<label th:text="#{pdfToImage.singleOrMultiple}"></label>
<select class="form-control" name="singleOrMultiple">
<option value="single" th:text="#{pdfToImage.single}"></option>
<option value="multiple" th:text="#{pdfToImage.multi}"></option>
</select>
</div>
<div class="form-group">
<label th:text="#{pdfToImage.colorType}"></label>
<select class="form-control" name="colorType">
<option value="color" th:text="#{pdfToImage.color}"></option>
<option value="greyscale" th:text="#{pdfToImage.grey}"></option>
<option value="blackwhite" th:text="#{pdfToImage.blackwhite}"></option>
</select>
</div>
<div class="form-group">
<label for="dpi">DPI:</label>
<input type="number" name="dpi" class="form-control" id="dpi" min="1" max="100" step="1" value="30" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{pdfToImage.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{xlsToPdf.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{xlsToPdf.header}"></h2>
<form method="post" enctype="multipart/form-data" th:action="@{xlsx-to-pdf}">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" required>
<label class="custom-file-label" for="fileInput" th:text="#{xlsToPdf.selectText.1}"></label>
</div>
<br> <br>
<button type="submit" class="btn btn-primary" th:text="#{imageToPDF.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Page Remover')}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>PDF Page remover</h2>
<form th:action="@{delete-pages}" method="post"
enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label for="pagesToDelete">Pages to delete (Enter a comma-separated
list of page numbers) :</label> <input type="text" class="form-control"
id="fileInput" name="pagesToDelete"
placeholder="(e.g. 1,2,6 or 1-10,15-30)" required>
</div>
<button type="submit" class="btn btn-primary">Delete
Pages</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Error! :(</title>
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
<style>
h1 {
text-align: center;
margin-top: 10%;
}
p {
text-align: center;
margin-top: 2em;
}
.button:hover {
background-color: #005b7f;
}
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
.feature-card {
border: 1px solid rgba(0, 0, 0, .125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.feature-card .card-text {
flex: 1;
}
#support-section {
background-color: #f9f9f9;
padding: 4rem;
margin-top: 1rem;
text-align: center;
}
#support-section h1 {
margin-top: 0;
}
#support-section p {
margin-top: 0;
}
#button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#github-button, #discord-button {
display: inline-block;
padding: 1rem 2rem;
margin: 1rem;
background-color: #008CBA;
color: #fff;
font-size: 1.2rem;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover, #discord-button:hover, #home-button:hover {
background-color: #005b7f;
}
#home-button {
display: block;
width: 200px;
height: 50px;
margin: 2em auto;
background-color: #008CBA;
color: white;
text-align: center;
line-height: 50px;
text-decoration: none;
font-weight: bold;
border-radius: 25px;
transition: all 0.3s ease-in-out;
}
</style>
</head>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<div class="container">
<div id="support-section">
<h1 class="display-2">Oops!</h1>
<p class="lead" th:if="${param.status == '404'}">We can't seem to find the page you're looking for.</p>
<p class="lead" th:unless="${param.status == '404'}">Something went wrong</p>
<br>
<h2>Need help / Found a issue?</h2>
<p>If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:</p>
<div id="button-group">
<a href="https://github.com/Frooodle/Stirling-PDF/issues" id="github-button" target="_blank">Submit a ticket on GitHub</a>
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank">Join our Discord server</a>
</div>
<a href="/" id="home-button">Go back to homepage</a>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,9 +1,5 @@
<div th:fragment="card" class="col-4 h-100">
<div class="dark-card card">
<div class="card-body">
<h5 class="card-title" th:text="${cardTitle}"></h5>
<p class="card-text" th:text="${cardText}"></p>
<a class="btn btn-primary" th:href="${cardLink}">Go</a>
</div>
</div>
<div th:fragment="card" class="feature-card">
<h5 class="card-title" th:text="${cardTitle}"></h5>
<p class="card-text" th:text="${cardText}"></p>
<a class="btn btn-primary" th:href="${cardLink}" th:text="#{goToPage}"></a>
</div>

View File

@@ -1,48 +1,60 @@
<head th:fragment="head">
<link rel="shortcut icon" href="favicon.svg">
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<!-- Metadata -->
<meta charset="UTF-8">
<link rel="stylesheet" th:href="@{dark-mode.css}" id="dark-mode-styles">
<script>
function toggleDarkMode() {
var checkbox = document.getElementById("toggle-dark-mode");
var darkModeStyles = document.getElementById("dark-mode-styles");
if (checkbox.checked) {
localStorage.setItem("dark-mode", "on");
darkModeStyles.disabled = false;
} else {
localStorage.setItem("dark-mode", "off");
darkModeStyles.disabled = true;
}
}
$(document).ready(function() {
var darkModeStyles = document.getElementById("dark-mode-styles");
var checkbox = document.getElementById("toggle-dark-mode");
if (localStorage.getItem("dark-mode") == "on") {
darkModeStyles.disabled = false;
checkbox.checked = true;
}
if (localStorage.getItem("dark-mode") == "off") {
darkModeStyles.disabled = true;
checkbox.checked = false;
}
});
</script>
<title th:text="'S-PDF ' + ${title}"></title>
<link rel="stylesheet" href="general.css">
<link rel="shortcut icon" href="favicon.svg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- jQuery -->
<script src="js/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="js/popper.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/bootstrap-icons.css">
<!-- PDF.js -->
<script src="pdfjs/pdf.min.js"></script>
<link href="pdfjs/pdf_viewer.min.css" rel="stylesheet">
<!-- Custom -->
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
<script>
function toggleDarkMode() {
var checkbox = document.getElementById("toggle-dark-mode");
var darkModeStyles = document.getElementById("dark-mode-styles");
if (checkbox.checked) {
localStorage.setItem("dark-mode", "on");
darkModeStyles.disabled = false;
} else {
localStorage.setItem("dark-mode", "off");
darkModeStyles.disabled = true;
}
}
$(document).ready(function() {
var darkModeStyles = document.getElementById("dark-mode-styles");
var checkbox = document.getElementById("toggle-dark-mode");
if (localStorage.getItem("dark-mode") == "on") {
darkModeStyles.disabled = false;
checkbox.checked = true;
}
if (localStorage.getItem("dark-mode") == "off") {
darkModeStyles.disabled = true;
checkbox.checked = false;
}
});
</script>
</head>
<th:block th:fragment="filelist">
<div id="fileList"></div>
<div id="fileList2"></div>
<script>
<div id="fileList"></div>
<div id="fileList2"></div>
<script>
var input = document.getElementById("fileInput");
var output = document.getElementById("fileList");
@@ -57,7 +69,7 @@
output.innerHTML = fileNames;
});
</script>
<script>
<script>
var input2 = document.getElementById("fileInput2");
var output2 = document.getElementById("fileList2");
if (input2 != null && !input2) {
@@ -75,7 +87,7 @@
</script>
<script>
<script>
if (dropContainer) {
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
evt.preventDefault();
@@ -95,4 +107,31 @@
};
}
</script>
</th:block>
<th:block th:fragment="fileSelector(name, multiple)">
<div class="custom-file-chooser">
<div class="custom-file">
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:multiple="${multiple}">
<label class="custom-file-label" th:for="${name}+'-input'" th:text="#{pdfPrompt}"></label>
</div>
</div>
<script th:inline="javascript">
$([[${"#"+name+"-input"}]]).on("change", function() {
const files = $(this).get(0).files;
const fileNames = Array.from(files).map(f => f.name).join(", ");
if (fileNames) {
$(this).siblings(".custom-file-label").addClass("selected").html(fileNames);
} else {
$(this).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
}
});
</script>
<style>
.custom-file-label {
padding-right: 90px;
}
</style>
</th:block>

View File

@@ -0,0 +1,74 @@
<th:block th:fragment="errorBanner">
<style>
#github-button,
#discord-button {
display: inline-block;
padding: 1rem 2rem;
background-color: #008CBA;
color: #fff;
text-align: center;
text-decoration: none;
border-radius: 3rem;
transition: all 0.3s ease-in-out;
}
#github-button:hover,
#discord-button:hover {
background-color: #005b7f;
}
</style>
<br th:if="${message}">
<div id="errorContainer" th:if="${message}" class="alert alert-danger alert-dismissible fade show" role="alert">
<h4 class="alert-heading" th:text="'Error: ' + ${status} + ' ' + ${error}"></h4>
<p th:text="${message} + ' for path: ' + ${path}"></p>
<button type="button" class="btn btn-danger" th:if="${trace}" onclick="toggletrace()">Show Stack Trace</button>
<button type="button" class="btn btn-secondary" th:if="${trace}" onclick="copytrace()">Copy Stack Trace</button>
<button type="button" class="close" data-dismiss="alert" aria-label="Close" onclick="dismissError()">
<span aria-hidden="true">&times;</span>
</button>
<!-- Stack trace section -->
<div id="trace" th:if="${trace}" style="max-height: 0; overflow: hidden;">
<div style="background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 3px; padding: 10px; margin-top: 5px;">
<pre id="traceContent" th:text="${trace}"></pre>
</div>
<!-- Buttons to submit a ticket on GitHub and join Discord server -->
<a href="https://github.com/Frooodle/Stirling-PDF/issues" id="github-button" target="_blank">Submit a ticket on GitHub</a>
<a href="https://discord.gg/Cn8pWhQRxZ" id="discord-button" target="_blank">Join our Discord server</a>
</div>
</div>
<script>
function toggletrace() {
var traceDiv = document.getElementById("trace");
if (traceDiv.style.maxHeight === "0px") {
traceDiv.style.maxHeight = "500px";
} else {
traceDiv.style.maxHeight = "0px";
}
adjustContainerHeight();
}
function copytrace() {
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
range.selectNode(traceContent);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
}
function dismissError() {
var errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "none";
errorContainer.style.height ="0";
}
function adjustContainerHeight() {
var errorContainer = document.getElementById("errorContainer");
var traceDiv = document.getElementById("trace");
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
}
</script>
</th:block>

View File

@@ -1,11 +1,6 @@
<div th:fragment="footer">
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.6.1/css/all.css">
<div th:fragment="footer">
<footer id="footer" class="text-center py-3">
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank"
class="mx-1"> <i class="fab fa-github fa-2x"></i>
</a> <a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank"
class="mx-1"> <i class="fab fa-docker fa-2x"></i>
</a>
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"><img src="images/github.svg"></img></a>
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"><img src="images/docker.svg"></img></a>
</footer>
</div>

View File

@@ -1,87 +1,133 @@
<div th:fragment="navbar">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#" th:href="@{/}">Stirling PDF</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarNav" aria-controls="navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{merge-pdfs}"
th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''">Merge
PDFs</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{split-pdfs}"
th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''">Split
PDFs</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{pdf-organizer}"
th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''">Page
Organizer</a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{rotate-pdf}"
th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''">Rotate PDF</a></li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Convert
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''">PDF to Image</a>
<a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''">Image to PDF</a>
</div>
</li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='add-password' OR ${currentPage}=='remove-password' OR ${currentPage}=='change-permissions' OR ${currentPage}=='add-watermark' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Security
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{add-password}" th:classappend="${currentPage}=='add-password' ? 'active' : ''">Add password (Encrypt)</a>
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''">Remove password (Decrypt)</a>
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''">Change permissions</a>
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''">Add Watermark</a>
</div>
</li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='delete-pages' OR ${currentPage}=='add-image' OR ${currentPage}=='compress-pdf' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Others
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''">Add
image to PDF</a>
<a class="dropdown-item" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''">Compress PDF</a>
<a class="dropdown-item" href="#" th:href="@{delete-pages}" th:classappend="${currentPage}=='delete-pages' ? 'active' : ''">Remove Pages</a>
</div>
</li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{add-image}"
th:classappend="${currentPage}=='add-image' ? 'active' : ''"></a></li>
<li class="nav-item"><a class="nav-link" href="#"
th:href="@{compress-pdf}"
th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''"></a></li>
<input type="checkbox" id="toggle-dark-mode" checked="true"
th:onclick="javascript:toggleDarkMode()">
<a class="nav-link" href="#" for="toggle-dark-mode">Dark Mode</a>
</ul>
</div>
</div>
</nav>
</div>
<div th:fragment="navbar">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#" th:href="@{/}">Stirling PDF</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarNav" aria-controls="navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{merge-pdfs}" th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''" th:text="#{home.merge.title}"></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{split-pdfs}" th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''" th:text="#{home.split.title}"></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{pdf-organizer}" th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''" th:text="#{home.pdfOrganiser.title}"></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{rotate-pdf}" th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''" th:text="#{home.rotate.title}"></a>
</li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='pdf-to-img' OR ${currentPage}=='img-to-pdf' OR ${currentPage}=='xlsx-to-pdf' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:text="#{navbar.convert}"></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''" th:text="#{home.pdfToImage.title}"></a>
<a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''" th:text="#{home.imageToPdf.title}"></a>
<a class="dropdown-item" href="#" th:href="@{xlsx-to-pdf}" th:classappend="${currentPage}=='xlsx-to-pdf' ? 'active' : ''" th:text="#{home.xlsToPdf.title}"></a>
</div>
</li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='add-password' OR ${currentPage}=='remove-password' OR ${currentPage}=='change-permissions' OR ${currentPage}=='add-watermark' OR ${currentPage}=='remove-watermark' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:text="#{navbar.security}"></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{add-password}" th:classappend="${currentPage}=='add-password' ? 'active' : ''" th:text="#{home.addPassword.title}"></a>
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''" th:text="#{home.removePassword.title}"></a>
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''" th:text="#{home.permissions.title}"></a>
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''" th:text="#{home.watermark.title}"></a>
<a class="dropdown-item" href="#" th:href="@{change-metadata}" th:classappend="${currentPage}=='change-metadata' ? 'active' : ''" th:text="#{home.changeMetadata.title}"></a>
</div>
</li>
<li class="nav-item dropdown" th:classappend="${currentPage}=='remove-pages' OR ${currentPage}=='add-image' OR ${currentPage}=='compress-pdf' ? 'active' : ''">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:text="#{navbar.other}"></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''" th:text="#{home.addImage.title}"></a>
<a class="dropdown-item" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:text="#{home.compressPdfs.title}"></a>
<a class="dropdown-item" href="#" th:href="@{remove-pages}" th:classappend="${currentPage}=='remove-pages' ? 'active' : ''" th:text="#{home.removePages.title}"></a>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''"></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''"></a>
</li>
<input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()">
<a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></a>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="bi bi-globe2"></i>
</a>
<div class="dropdown-menu" aria-labelledby="languageDropdown">
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_US">English (US)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_GB">English (UK)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="ar_AR">العربية</a>
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="de_DE">Deutsch</a>
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="fr_FR">Français</a>
</div>
</li>
</ul>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get the dropdown items
var dropdownItems = document.querySelectorAll('.lang_dropdown-item');
// Loop through the dropdown items
for (var i = 0; i < dropdownItems.length; i++) {
dropdownItems[i].classList.remove('active');
if (dropdownItems[i].dataset.languageCode === localStorage.getItem('languageCode')) {
dropdownItems[i].classList.add('active');
}
// Add a click event listener to each dropdown item
dropdownItems[i].addEventListener('click', function(event) {
// Prevent the default link behavior
event.preventDefault();
// Get the language code
var languageCode = this.dataset.languageCode;
// Save the language code to local storage
localStorage.setItem('languageCode', languageCode);
// Get the current URL
var currentUrl = window.location.href;
// Check if the URL already contains a "?lang" query parameter
if (currentUrl.indexOf('?lang=') === -1) {
// Update the URL with the "?lang" query parameter
window.location.href = currentUrl + '?lang=' + languageCode;
} else {
// Replace the "?lang" query parameter with the new value
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode);
}
});
}
});
</script>
</div>
</div>
</nav>
<div th:insert="~{fragments/errorBanner.html :: errorBanner}"></div>
</div>

View File

@@ -1,61 +1,70 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='')}"></th:block>
<style>
.features-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr));
gap: 25px 30px;
}
.feature-card {
border: 1px solid rgba(0, 0, 0, .125);
border-radius: 0.25rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.feature-card .card-text {
flex: 1;
}
</style>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<!-- Jumbotron -->
<div class="jumbotron jumbotron-fluid" id="jumbotron">
<div class="container">
<h1 class="display-4">Stirling PDF</h1>
<p class="lead">Your locally hosted one-stop-shop for all your
PDF needs. (Made 100% in ChatGPT in 1 day as a experiment)</p>
</div>
</div>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<!-- Jumbotron -->
<div class="jumbotron jumbotron-fluid" id="jumbotron">
<div class="container">
<h1 class="display-4">Stirling PDF</h1>
<p class="lead" th:text="#{home.desc}"></p>
</div>
</div>
<!-- Features -->
<div class="container">
<div class="row h-100">
<div th:replace="~{fragments/card :: card(cardTitle='Merge PDFs', cardText='Easily merge multiple PDFs into one.', cardLink='merge-pdfs')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Split PDFs', cardText='Split PDFs into multiple documents', cardLink='split-pdfs')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Rotate PDFs', cardText='Easily rotate your PDFs.', cardLink='rotate-pdf')}"></div>
</div>
<!-- Features -->
<div class="features-container container">
<div th:replace="~{fragments/card :: card(cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf')}"></div>
<br>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer')}"></div>
<div class="row h-100">
<div th:replace="~{fragments/card :: card(cardTitle='Image to PDF', cardText='Convert a images (PNG, JPEG, GIF) to PDF.', cardLink='img-to-pdf')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='PDF to Image', cardText='Convert a PDF to a image. (PNG, JPEG, GIF)', cardLink='pdf-to-img')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='PDF Organizer', cardText='Remove/Rearrange pages in any order', cardLink='pdf-organizer')}"></div>
</div>
<br>
<div class="row h-100">
<div th:replace="~{fragments/card :: card(cardTitle='Add image onto PDF', cardText='Adds a image onto a set location on the PDF (Work in progress)', cardLink='add-image')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Add Watermark', cardText='Add a custom watermark to your PDF document.', cardLink='add-watermark')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Change Permissions', cardText='Change the permissions of your PDF document, such as print, copy, edit, etc.', cardLink='change-permissions')}"></div>
</div>
<br>
<div class="row h-100">
<div th:replace="~{fragments/card :: card(cardTitle='Remove Pages', cardText='Delete unwanted pages from your PDF document.', cardLink='remove-pages')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Add Password', cardText='Encrypt your PDF document with a password.', cardLink='add-password')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle='Remove Password', cardText='Remove password protection from your PDF document.', cardLink='remove-password')}"></div>
</div>
<br>
<div class="row h-100">
<div th:replace="~{fragments/card :: card(cardTitle='Compress PDFs', cardText='Compress PDFs to reduce their file size.', cardLink='compress-pdf')}"></div>
</div>
<br>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.xlsToPdf.title}, cardText=#{home.xlsToPdf.desc}, cardLink='xlsx-to-pdf')}"></div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata')}"></div>
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions')}"></div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,133 +1,127 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Merge PDFs')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{merge.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container" id="dropContainer">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Merge multiple PDFs (2+)</h2>
<form action="merge-pdfs" method="post"
enctype="multipart/form-data">
<div class="form-group">
<label>Select (or drag & drop) all PDFs to merge</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" multiple required> <label
class="custom-file-label">Choose PDFs</label>
</div>
</div>
<div class="form-group">
<ul id="selectedFiles" class="list-group"></ul>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary">Merge</button>
</div>
</form>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container" id="dropContainer">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{merge.header}"></h2>
<form action="merge-pdfs" method="post" enctype="multipart/form-data">
<div class="form-group">
<label th:text="#{multiPdfDropPrompt}"></label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput" name="fileInput" multiple required>
<label class="custom-file-label" th:text="#{pdfPrompt}"></label>
</div>
</div>
<div class="form-group">
<ul id="selectedFiles" class="list-group"></ul>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" th:text="#{merge.submit}"></button>
</div>
</form>
<script>
document
.getElementById(
"fileInput")
.addEventListener(
"change",
function() {
var files = this.files;
var list = document
.getElementById("selectedFiles");
list.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var item = document
.createElement("li");
item.className = "list-group-item d-flex justify-content-between align-items-center";
item.textContent = files[i].name;
item.innerHTML += '<div><button class="btn btn-secondary move-up"><i class="fas fa-arrow-up"></i></button> <button class="btn btn-secondary move-down"><i class="fas fa-arrow-down"></i></button></div>';
list
.appendChild(item);
}
var moveUpButtons = document
.querySelectorAll(".move-up");
for (var i = 0; i < moveUpButtons.length; i++) {
moveUpButtons[i]
.addEventListener(
"click",
function(
event) {
event
.preventDefault();
var parent = this.parentNode.parentNode;
var grandParent = parent.parentNode;
if (parent.previousElementSibling) {
grandParent
.insertBefore(
parent,
parent.previousElementSibling);
updateFiles();
}
});
}
var moveDownButtons = document
.querySelectorAll(".move-down");
for (var i = 0; i < moveDownButtons.length; i++) {
moveDownButtons[i]
.addEventListener(
"click",
function(
event) {
event
.preventDefault();
var parent = this.parentNode.parentNode;
var grandParent = parent.parentNode;
if (parent.nextElementSibling) {
grandParent
.insertBefore(
parent.nextElementSibling,
parent);
updateFiles();
}
});
}
function updateFiles() {
var dataTransfer = new DataTransfer();
var liElements = document
.querySelectorAll("#selectedFiles li");
<script>
document
.getElementById("fileInput")
.addEventListener(
"change",
function() {
var files = this.files;
var list = document
.getElementById("selectedFiles");
list.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var item = document
.createElement("li");
item.className = "list-group-item d-flex justify-content-between align-items-center";
item.textContent = files[i].name;
item.innerHTML += '<div><button class="btn btn-secondary move-up">Move Up</button> <button class="btn btn-secondary move-down">Move Down</button></div>';
list.appendChild(item);
}
var moveUpButtons = document
.querySelectorAll(".move-up");
for (var i = 0; i < moveUpButtons.length; i++) {
moveUpButtons[i]
.addEventListener(
"click",
function(event) {
event
.preventDefault();
var parent = this.parentNode.parentNode;
var grandParent = parent.parentNode;
if (parent.previousElementSibling) {
grandParent
.insertBefore(
parent,
parent.previousElementSibling);
updateFiles();
}
});
}
var moveDownButtons = document
.querySelectorAll(".move-down");
for (var i = 0; i < moveDownButtons.length; i++) {
moveDownButtons[i]
.addEventListener(
"click",
function(event) {
event
.preventDefault();
var parent = this.parentNode.parentNode;
var grandParent = parent.parentNode;
if (parent.nextElementSibling) {
grandParent
.insertBefore(
parent.nextElementSibling,
parent);
updateFiles();
}
});
}
function updateFiles() {
var dataTransfer = new DataTransfer();
var liElements = document
.querySelectorAll("#selectedFiles li");
for (var i = 0; i < liElements.length; i++) {
var fileNameFromList = liElements[i].innerText
.replace(
"\nMove Up Move Down",
"");
var fileFromFiles
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file.name === fileNameFromList) {
dataTransfer.items
.add(file);
break;
for (var i = 0; i < liElements.length; i++) {
var fileNameFromList = liElements[i].innerText
.replace(
"\nMove Up Move Down",
"");
var fileFromFiles
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file.name === fileNameFromList) {
dataTransfer.items
.add(file);
break;
}
}
}
document
.getElementById("fileInput").files = dataTransfer.files;
}
document
.getElementById("fileInput").files = dataTransfer.files;
}
});
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
});
</script>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,44 +1,34 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Organizer')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{pdfOrganiser.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>PDF Page Organizer</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{pdfOrganiser.header}"></h2>
<form th:action="@{rearrange-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label for="pageOrder" th:text="#{pageOrderPrompt}"></label>
<input type="text" class="form-control" id="fileInput" name="pageOrder" placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{pdfOrganiser.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
<form th:action="@{rearrange-pages}" method="post"
enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="form-group">
<label for="pageOrder">Page Order (Enter a comma-separated
list of page numbers) :</label> <input type="text" class="form-control"
id="fileInput" name="pageOrder"
placeholder="(e.g. 1,3,2 or 4-8,2,10-12)" required>
</div>
<button type="submit" class="btn btn-primary">Rearrange
Pages</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{pageRemover.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{pageRemover.header}"></h2>
<form th:action="@{remove-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label for="pagesToDelete" th:text="#{pageRemover.pagesToDelete}"></label>
<input type="text" class="form-control" id="fileInput" name="pagesToDelete" placeholder="(e.g. 1,2,6 or 1-10,15-30)" required>
</div>
<button type="submit" class="btn btn-primary" th:text="#{pageRemover.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,49 +1,132 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Rotate')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{rotate.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Rotate PDF</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{rotate.header}"></h2>
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<input type="hidden" id="angleInput" name="angle" value="0">
<div id="editSection" style="display: none">
<div class="previewContainer">
<img id="pdf-preview" />
</div>
<form action="#" th:action="@{rotate-pdf}" th:object="${rotateForm}"
method="post" enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<div class="buttonContainer">
<button type="button" class="btn btn-secondary" onclick="rotate(-90)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z" />
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z" />
</svg>
</button>
<button type="submit" class="btn btn-primary" th:text="#{rotate.submit}"></button>
<button type="button" class="btn btn-secondary" onclick="rotate(90)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
</svg>
</button>
</div>
</div>
</form>
<label for="angle">Select rotation angle (in multiples of
90 degrees):</label> <select id="angle" class="form-control" name="angle">
<option value="90">90</option>
<option value="180">180</option>
<option value="270">270</option>
<option value="-90">-90</option>
<option value="-180">-180</option>
<option value="-270">-270</option>
</select> <br>
<button type="submit" class="btn btn-primary">Rotate</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<script>
const angleInput = document.getElementById("angleInput");
const fileInput = document.getElementById("fileInput-input");
const preview = document.getElementById("pdf-preview");
fileInput.addEventListener("change", async function() {
console.log("loading pdf");
document.querySelector("#editSection").style.display = "";
var url = URL.createObjectURL(fileInput.files[0])
const pdf = await pdfjsLib.getDocument(url).promise;
const page = await pdf.getPage(1);
const canvas = document.createElement("canvas");
// set the canvas size to the size of the page
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];
}
// render the page onto the canvas
var renderContext = {
canvasContext: canvas.getContext("2d"),
viewport: page.getViewport({ scale: 1 })
};
await page.render(renderContext).promise;
preview.src = canvas.toDataURL();
});
function rotate(deg) {
var lastTransform = preview.style.rotate;
if (!lastTransform) {
lastTransform = "0";
}
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ''));
const newAngle = lastAngle + deg;
preview.style.rotate = newAngle + "deg";
angleInput.value = newAngle;
}
</script>
<style>
#pdf-preview {
margin: 0 auto;
display: block;
max-width: calc(100% - 30px);
max-height: calc(100% - 30px);
box-shadow: 0 0 4px rgba(100, 100, 100, .25);
transition: rotate .3s;
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
}
.previewContainer {
aspect-ratio: 1;
width: 100%;
border: 1px solid rgba(0, 0, 0, .125);
border-radius: 0.25rem;
margin: 1rem 0;
padding: 15px;
display: block;
overflow: hidden;
position: relative;
}
.buttonContainer {
display: flex;
justify-content: space-around;
}
</style>
</body>
</html>

View File

@@ -1,103 +1,80 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Add Password')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{addPassword.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Add password (Encrypt)</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{addPassword.header}"></h2>
<form action="add-password" method="post"
enctype="multipart/form-data">
<div class="form-group">
<label>Select PDF to encrypt</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label">Choose PDF</label>
</div>
</div>
<div class="form-group">
<label>Password</label> <input type="password"
class="form-control" id="password" name="password" required>
</div>
<div class="form-group">
<label>Encryption Key Length</label> <select class="form-control"
id="keyLength" name="keyLength">
<option value="40">40</option>
<option value="128">128</option>
<option value="256">256</option>
</select> <small class="form-text text-muted">Higher values are
stronger, but lower values have better compatibility.</small>
</div>
<div class="form-group">
<label>Permissions to set</label>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canAssembleDocument" name="canAssembleDocument"> <label
class="form-check-label" for="canAssembleDocument">
Prevent assembly of document </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canExtractContent" name="canExtractContent"> <label
class="form-check-label" for="canExtractContent">
Prevent content extraction </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canExtractForAccessibility"
name="canExtractForAccessibility"> <label
class="form-check-label" for="canExtractForAccessibility">
Prevent extraction for accessibility </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canFillInForm" name="canFillInForm"> <label
class="form-check-label" for="canFillInForm"> Prevent
filling in form </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModify"
name="canModify"> <label class="form-check-label"
for="canModify"> Prevent modification </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canModifyAnnotations" name="canModifyAnnotations"> <label
class="form-check-label" for="canModifyAnnotations">
Prevent annotation modification </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrint"
name="canPrint"> <label class="form-check-label"
for="canPrint"> Prevent printing </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canPrintFaithful" name="canPrintFaithful"> <label
class="form-check-label" for="canPrintFaithful"> Prevent
printing different formats </label>
</div>
<form action="add-password" method="post" enctype="multipart/form-data">
<div class="form-group">
<label th:text="#{addPassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
</div>
<div class="form-group">
<label th:text="#{addPassword.selectText.2}"></label> <input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="form-group">
<label th:text="#{addPassword.selectText.3}"></label> <select class="form-control" id="keyLength" name="keyLength">
<option value="40">40</option>
<option value="128">128</option>
<option value="256">256</option>
</select> <small class="form-text text-muted" th:text="#{addPassword.selectText.4}"></small>
</div>
<div class="form-group">
<label th:text="#{addPassword.selectText.5}"></label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label class="form-check-label" for="canAssembleDocument" th:text="#{addPassword.selectText.6}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canExtractContent" name="canExtractContent">
<label class="form-check-label" for="canExtractContent" th:text="#{addPassword.selectText.7}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label class="form-check-label" for="canExtractForAccessibility" th:text="#{addPassword.selectText.8}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canFillInForm" name="canFillInForm">
<label class="form-check-label" for="canFillInForm" th:text="#{addPassword.selectText.9}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModify" name="canModify">
<label class="form-check-label" for="canModify" th:text="#{addPassword.selectText.10}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label class="form-check-label" for="canModifyAnnotations" th:text="#{addPassword.selectText.11}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrint" name="canPrint">
<label class="form-check-label" for="canPrint" th:text="#{addPassword.selectText.12}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label class="form-check-label" for="canPrintFaithful" th:text="#{addPassword.selectText.13}"></label>
</div>
</div>
<br />
<div class="form-group text-center">
<button type="submit" class="btn btn-primary">Encrypt</button>
</div>
</div>
<br />
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" th:text="#{addPassword.submit}"></button>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,56 +1,52 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Add Watermark')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{watermark.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Add Watermark</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{watermark.header}"></h2>
<form method="post" enctype="multipart/form-data" action="add-watermark">
<div class="form-group">
<label>Select PDF to add watermark to:</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label">Choose PDF</label>
</div>
</div>
<div class="form-group">
<label for="watermarkText">Watermark Text:</label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required/>
</div>
<div class="form-group">
<label for="fontSize">Font Size:</label>
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30"/>
</div>
<div class="form-group">
<label for="rotation">Rotation (0-360):</label>
<input type="text" id="rotation" name="rotation" class="form-control" value="45"/>
</div>
<div class="form-group">
<label for="widthSpacer">widthSpacer (Space between each watermark horizontally):</label>
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50"/>
</div>
<div class="form-group">
<label for="heightSpacer">heightSpacer (Space between each watermark vertically):</label>
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50"/>
</div>
<div class="form-group text-center">
<input type="submit" value="Add Watermark" class="btn btn-primary"/>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<form method="post" enctype="multipart/form-data" action="add-watermark">
<div class="form-group">
<label th:text="#{watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
</div>
<div class="form-group">
<label for="watermarkText" th:text="#{watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required />
</div>
<div class="form-group">
<label for="fontSize" th:text="#{watermark.selectText.3}"></label>
<input type="text" id="fontSize" name="fontSize" class="form-control" value="30" />
</div>
<div class="form-group">
<label for="rotation" th:text="#{watermark.selectText.4}"></label>
<input type="text" id="rotation" name="rotation" class="form-control" value="45" />
</div>
<div class="form-group">
<label for="widthSpacer" th:text="#{watermark.selectText.5}"></label>
<input type="text" id="widthSpacer" name="widthSpacer" class="form-control" value="50" />
</div>
<div class="form-group">
<label for="heightSpacer" th:text="#{watermark.selectText.6}"></label>
<input type="text" id="heightSpacer" name="heightSpacer" class="form-control" value="50" />
</div>
<div class="form-group text-center">
<input type="submit" th:value="#{watermark.submit}" class="btn btn-primary" />
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,263 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{changeMetadata.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{changeMetadata.header}"></h2>
<form method="post" id="form1" enctype="multipart/form-data" th:action="@{/update-metadata}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<p class="text-muted" th:text="#{changeMetadata.selectText.1}"></p>
<div class="form-group-inline form-check">
<input type="checkbox" class="form-check-input" id="deleteAll" name="deleteAll">
<label class="ml-3" for="deleteAll" th:text="#{changeMetadata.selectText.2}" ></label>
</div>
<div class="form-group-inline form-check">
<input type="checkbox" class="form-check-input" id="customModeCheckbox">
<label class="ml-3" for="customModeCheckbox" th:text="#{changeMetadata.selectText.3}"></label>
</div>
<div class="form-group">
<label class="form-check-label" for="author" th:text="#{changeMetadata.author}"></label>
<input type="text" class="form-control" id="author" name="author">
</div>
<div class="form-group">
<label class="form-check-label" for="creationDate" th:text="#{changeMetadata.creationDate}"></label>
<input type="text" class="form-control" id="creationDate" name="creationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="form-group">
<label class="form-check-label" for="creator" th:text="#{changeMetadata.creator}"></label>
<input type="text" class="form-control" id="creator" name="creator">
</div>
<div class="form-group">
<label class="form-check-label" for="keywords" th:text="#{changeMetadata.keywords}"></label>
<input type="text" class="form-control" id="keywords" name="keywords">
</div>
<div class="form-group">
<label class="form-check-label" for="modificationDate" th:text="#{changeMetadata.modDate}"></label>
<input type="text" class="form-control" id="modificationDate" name="modificationDate" placeholder="2020/12/25 18:30:59">
</div>
<div class="form-group">
<label class="form-check-label" for="producer" th:text="#{changeMetadata.producer}"></label>
<input type="text" class="form-control" id="producer" name="producer">
</div>
<div class="form-group">
<label class="form-check-label" for="subject" th:text="#{changeMetadata.subject}"></label>
<input type="text" class="form-control" id="subject" name="subject">
</div>
<div class="form-group">
<label class="form-check-label" for="title" th:text="#{changeMetadata.title}"></label>
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="form-group">
<label class="form-check-label" for="trapped" th:text="#{changeMetadata.trapped}"></label>
<select class="form-control" id="trapped" name="trapped">
<option value="True" th:text="#{true}"></option>
<option value="False" th:text="#{false}" selected></option>
<option value="Unknown" th:text="#{unknown}"></option>
</select>
</div>
<div id="customMetadata" style="display: none;">
<h3 th:text="#{changeMetadata.selectText.4}"></h3>
<div class="form-group" id="otherMetadataEntries"></div>
</div>
<div id="customMetadataEntries"></div>
<button type="button" class="btn btn-secondary" id="addMetadataBtn" th:text="#{changeMetadata.selectText.5}"></button>
<br>
<br>
<button class="btn btn-primary" type="submit" th:text="#{changeMetadata.submit}"></button>
<script>
const deleteAllCheckbox = document.querySelector("#deleteAll");
const inputs = document.querySelectorAll(".form-control");
const customMetadataDiv = document.getElementById('customMetadata');
const otherMetadataEntriesDiv = document.getElementById('otherMetadataEntries');
deleteAllCheckbox.addEventListener("change", function(event) {
if (event.target !== deleteAllCheckbox) {
return;
}
inputs.forEach(input => {
if (input === deleteAllCheckbox) {
return;
}
input.disabled = deleteAllCheckbox.checked;
});
});
const customModeCheckbox = document.getElementById('customModeCheckbox');
const addMetadataBtn = document.getElementById("addMetadataBtn");
const customMetadataFormContainer = document.getElementById("customMetadataEntries");
var count = 1;
const fileInput = document.querySelector("#fileInput-input");
const authorInput = document.querySelector("#author");
const creationDateInput = document.querySelector("#creationDate");
const creatorInput = document.querySelector("#creator");
const keywordsInput = document.querySelector("#keywords");
const modificationDateInput = document.querySelector("#modificationDate");
const producerInput = document.querySelector("#producer");
const subjectInput = document.querySelector("#subject");
const titleInput = document.querySelector("#title");
const trappedInput = document.querySelector("#trapped");
var lastPDFFileMeta = null;
fileInput.addEventListener("change", async function() {
while (otherMetadataEntriesDiv.firstChild) {
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
while (customMetadataFormContainer.firstChild) {
customMetadataFormContainer.removeChild(customMetadataFormContainer.firstChild);
}
const file = this.files[0];
var url = URL.createObjectURL(file)
const pdf = await pdfjsLib.getDocument(url).promise;
const pdfMetadata = await pdf.getMetadata();
lastPDFFile = pdfMetadata?.info
console.log(pdfMetadata);
if(!pdfMetadata?.info?.Custom || pdfMetadata?.info?.Custom.size == 0) {
customModeCheckbox.disabled = true;
customModeCheckbox.checked = false;
} else {
customModeCheckbox.disabled = false;
}
authorInput.value = pdfMetadata?.info?.Author;
creationDateInput.value = convertDateFormat(pdfMetadata?.info?.CreationDate);
creatorInput.value = pdfMetadata?.info?.Creator;
keywordsInput.value = pdfMetadata?.info?.Keywords;
modificationDateInput.value = convertDateFormat(pdfMetadata?.info?.ModDate);
producerInput.value = pdfMetadata?.info?.Producer;
subjectInput.value = pdfMetadata?.info?.Subject;
titleInput.value = pdfMetadata?.info?.Title;
console.log(pdfMetadata?.info);
const trappedValue = pdfMetadata?.info?.Trapped;
// Get all options in the select element
const options = trappedInput.options;
// Loop through all options to find the one with a matching value
for (let i = 0; i < options.length; i++) {
if (options[i].value === trappedValue) {
options[i].selected = true;
break;
}
}
addExtra();
});
addMetadataBtn.addEventListener("click", () => {
const keyInput = document.createElement("input");
keyInput.type = "text";
keyInput.placeholder = 'Key';
keyInput.className = "form-control";
keyInput.name = "customKey" + count;
const valueInput = document.createElement("input");
valueInput.type = "text";
valueInput.placeholder = 'Value';
valueInput.className = "form-control";
valueInput.name = "customValue" + count;
count = count + 1;
const formGroup = document.createElement("div");
formGroup.className = "form-group";
formGroup.appendChild(keyInput);
formGroup.appendChild(valueInput);
customMetadataFormContainer.appendChild(formGroup);
});
function convertDateFormat(dateTimeString) {
if (!dateTimeString || dateTimeString.length < 17) {
return dateTimeString;
}
const year = dateTimeString.substring(2, 6);
const month = dateTimeString.substring(6, 8);
const day = dateTimeString.substring(8, 10);
const hour = dateTimeString.substring(10, 12);
const minute = dateTimeString.substring(12, 14);
const second = dateTimeString.substring(14, 16);
return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second;
}
function addExtra() {
const event = document.getElementById("customModeCheckbox");
if (event.checked && lastPDFFile.Custom != null) {
customMetadataDiv.style.display = 'block';
for (const [key, value] of Object.entries(lastPDFFile.Custom)) {
if (key === 'Author' || key === 'CreationDate' || key === 'Creator' || key === 'Keywords' || key === 'ModDate' || key === 'Producer' || key === 'Subject' || key === 'Title' || key === 'Trapped') {
continue;
}
const entryDiv = document.createElement('div');
entryDiv.className = 'form-group';
entryDiv.innerHTML = `<div class="form-group"><label class="form-check-label" for="${key}">${key}:</label><input name="${key}" value="${value}" type="text" class="form-control" id="${key}"></div>`;
otherMetadataEntriesDiv.appendChild(entryDiv);
}
} else {
customMetadataDiv.style.display = 'none';
while (otherMetadataEntriesDiv.firstChild) {
otherMetadataEntriesDiv.removeChild(otherMetadataEntriesDiv.firstChild);
}
}
}
customModeCheckbox.addEventListener('change', (event) => {
addExtra();
});
</script>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,93 +1,71 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Change Permissions')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{permissions.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Change permissions</h2>
<p>Warning to have these permissions be
unchangeable it is recommended to set them with a password via the
add-password page</p>
<form action="add-password" method="post"
enctype="multipart/form-data">
<div class="form-group">
<label>Select PDF to change permissions</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput"> <label class="custom-file-label">Choose
PDF</label>
</div>
</div>
<div class="form-group">
<label>Permissions to set</label>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canAssembleDocument" name="canAssembleDocument"> <label
class="form-check-label" for="canAssembleDocument">
Prevent assembly of document </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canExtractContent" name="canExtractContent"> <label
class="form-check-label" for="canExtractContent">
Prevent content extraction </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canExtractForAccessibility"
name="canExtractForAccessibility"> <label
class="form-check-label" for="canExtractForAccessibility">
Prevent extraction for accessibility </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canFillInForm" name="canFillInForm"> <label
class="form-check-label" for="canFillInForm"> Prevent
filling in form </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModify"
name="canModify"> <label class="form-check-label"
for="canModify"> Prevent modification </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canModifyAnnotations" name="canModifyAnnotations"> <label
class="form-check-label" for="canModifyAnnotations">
Prevent annotation modification </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrint"
name="canPrint"> <label class="form-check-label"
for="canPrint"> Prevent printing </label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="canPrintFaithful" name="canPrintFaithful"> <label
class="form-check-label" for="canPrintFaithful"> Prevent
printing different formats </label>
</div>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{permissions.header}"></h2>
<p th:text="#{permissions.warning}"></p>
<form action="add-password" method="post" enctype="multipart/form-data">
<div class="form-group">
<label th:text="#{permissions.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
</div>
<div class="form-group">
<label th:text="#{permissions.selectText.2}"></label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canAssembleDocument" name="canAssembleDocument">
<label class="form-check-label" for="canAssembleDocument" th:text="#{permissions.selectText.3}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canExtractContent" name="canExtractContent">
<label class="form-check-label" for="canExtractContent" th:text="#{permissions.selectText.4}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canExtractForAccessibility" name="canExtractForAccessibility">
<label class="form-check-label" for="canExtractForAccessibility" th:text="#{permissions.selectText.5}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canFillInForm" name="canFillInForm">
<label class="form-check-label" for="canFillInForm" th:text="#{permissions.selectText.6}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModify" name="canModify">
<label class="form-check-label" for="canModify" th:text="#{permissions.selectText.7}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canModifyAnnotations" name="canModifyAnnotations">
<label class="form-check-label" for="canModifyAnnotations" th:text="#{permissions.selectText.8}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrint" name="canPrint">
<label class="form-check-label" for="canPrint" th:text="#{permissions.selectText.9}"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="canPrintFaithful" name="canPrintFaithful">
<label class="form-check-label" for="canPrintFaithful" th:text="#{permissions.selectText.10}"></label>
</div>
</div>
<br />
<div class="form-group text-center">
<button type="submit" class="btn btn-primary">Change</button>
</div>
</div>
<br />
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" th:text="#{permissions.submit}"></button>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,38 +1,37 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Remove password')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{removePassword.title})}"></th:block>
<body> <div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2>Remove password (Decrypt)</h2>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{removePassword.header}"></h2>
<form action="add-password" method="post"
enctype="multipart/form-data">
<div class="form-group">
<label>Select PDF to Decrypt</label>
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label">Choose PDF</label>
</div>
</div>
<div class="form-group">
<label>Password</label> <input type="password"
class="form-control" id="password" name="password" required>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<form action="remove-password" method="post" enctype="multipart/form-data">
<div class="form-group">
<label th:text="#{removePassword.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
</div>
<div class="form-group">
<label th:text="#{removePassword.selectText.2}"></label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<br />
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" th:text="#{removePassword.submit}"></button>
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{remove-watermark.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 th:text="#{remove-watermark.header}"></h2>
<form method="post" enctype="multipart/form-data" action="remove-text">
<div class="form-group">
<label th:text="#{remove-watermark.selectText.1}"></label>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
</div>
<div class="form-group">
<label for="watermarkText" th:text="#{remove-watermark.selectText.2}"></label>
<input type="text" id="watermarkText" name="watermarkText" class="form-control" placeholder="Stirling-PDF" required />
</div>
<div class="form-group text-center">
<input type="submit" th:value="#{remove-watermark.submit}" class="btn btn-primary" />
</div>
</form>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>

View File

@@ -1,53 +1,44 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html th:lang="${#locale.language}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title='Split')}"></th:block>
<th:block th:insert="~{fragments/common :: head(title=#{split.title})}"></th:block>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br>
<br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h1>Split PDF</h1>
<p>The numbers you select are the page number you wish to do a
split on</p>
<p>As such selecting 1,3,7-8 would split a 10 page document into
6 separate PDFS with:</p>
<p>Document #1: Page 1</p>
<p>Document #2: Page 2 and 3</p>
<p>Document #3: Page 4, 5 and 6</p>
<p>Document #4: Page 7</p>
<p>Document #5: Page 8</p>
<p>Document #6: Page 9 and 10</p>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<h1 th:text="#{split.header}"></h1>
<p th:text="#{split.desc.1}"></p>
<p th:text="#{split.desc.2}"></p>
<p th:text="#{split.desc.3}"></p>
<p th:text="#{split.desc.4}"></p>
<p th:text="#{split.desc.5}"></p>
<p th:text="#{split.desc.6}"></p>
<p th:text="#{split.desc.7}"></p>
<p th:text="#{split.desc.8}"></p>
<form th:action="@{split-pages}" method="post"
enctype="multipart/form-data">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput"
name="fileInput" required> <label
class="custom-file-label" for="fileInput">Choose PDF</label>
</div>
<form th:action="@{split-pages}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
<div class="form-group">
<label for="pages">Enter pages to split on:</label> <input
type="text" class="form-control" id="pages" name="pages"
placeholder="1,3,5-10" required>
</div>
<br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
<div class="form-group">
<label for="pages" th:text="#{split.splitPages}"></label>
<input type="text" class="form-control" id="pages" name="pages" placeholder="1,3,5-10" required>
</div>
<br>
<button type="submit" class="btn btn-primary" th:text="#{split.submit}"></button>
</form>
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
</div>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div>
</div>
</body>
</html>