diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8143e84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +## Hidden files +.* diff --git a/README.md b/README.md index a54b296..936391c 100644 --- a/README.md +++ b/README.md @@ -1,120 +1,351 @@ -# pdfScale.sh -Bash Script to scale PDFs from the command line. -Uses ghostscript to create a scaled version of the pdf input. -The "paper" size does not change, just the elements are resized. +# pdfScale 2 +Bash Script to ***scale*** and/or ***resize*** PDFs from the command line. +Uses ghostscript (`gs`) to create a scaled and/or resized version of the pdf input. + +In `scaling mode`, the PDF paper size does not change, just the elements are scaled. +In `resize mode`, the PDF paper will be changed and fit-to-page will be applied. +In `mixed mode`, the PDF will first be `resized` then `scaled` with two Ghostscript calls. +A temporary file is used in `mixed mode`, at the target location. -## Dependencies -The script uses `basename`, `cat`, `grep`, `bc`, `head` and `gs` (ghostscript). -You probably have everything installed already, except for ghostscript. -Optional dependencies are `imagemagick`, `pdfinfo` and `mdls` (Mac). +## Example Runs -##### apt-get +#### Checking File Information ``` -sudo apt-get install ghostscript bc +$ ./pdfScale.sh -i ../input-nup.pdf +pdfScale.sh v2.0.0 - Paper Sizes +------------+----------------------------- + File | input-nup.pdf + Paper Type | A4 Landscape +------------+----------------------------- + | WIDTH x HEIGHT + Points | 842 x 595 + Milimeters | 297 x 210 + Inches | 11.69 x 8.26 ``` -##### yum +#### Resize to A0 and Scale by 1.05 (+5%) ``` -sudo yum install ghostscript bc +$ pdfscale -v -r a0 -s 1.05 ../mixsync\ manual\ v1-2-3.pdf +pdfscale v2.0.0 - Verbose Execution + Mixed Tasks: Resize & Scale + Input File: ../mixsync manual v1-2-3.pdf + Output File: ../mixsync manual v1-2-3.A0.SCALED.pdf + Get Page Size: Adaptive Enabled + Method: Grep + Source Width: 842 postscript-points + Source Height: 595 postscript-points + Auto Rotate: PageByPage + Flip Detect: Wrong orientation detected! + Inverting Width <-> Height + Run Resizing: A0 ( 3370 x 2384 ) pts + New Width: 3370 postscript-points + New Height: 2384 postscript-points + Scale Factor: 1.05 + Translation X: -80.236330 + Translation Y: -56.760656 + Run Scaling: 5 % + Final Status: File created successfully ``` -##### homebrew MacOS +#### Scale by 0.9 (-10%) ``` -brew install ghostscript +$ pdfscale -v -v -s 0.9 ../input-nup.pdf "../My Glorius PDF" +2017-05-15:03:03:23 | pdfscale v2.0.0 - Verbose Execution +2017-05-15:03:03:23 | Single Task: Scale PDF Contents +2017-05-15:03:03:23 | Input File: ../input-nup.pdf +2017-05-15:03:03:23 | Output File: ../My Glorius PDF.pdf +2017-05-15:03:03:23 | Get Page Size: Adaptive Enabled +2017-05-15:03:03:23 | Method: Grep +2017-05-15:03:03:23 | Failed +2017-05-15:03:03:23 | Method: Mac Quartz mdls +2017-05-15:03:03:23 | Source Width: 842 postscript-points +2017-05-15:03:03:23 | Source Height: 595 postscript-points +2017-05-15:03:03:23 | Scale Factor: 0.9 (manual) +2017-05-15:03:03:23 | Translation X: 46.777310 +2017-05-15:03:03:23 | Translation Y: 33.055225 +2017-05-15:03:03:23 | Run Scaling: -10 % +2017-05-15:03:03:24 | Final Status: File created successfully ``` -##### Optionals -From version 1.4.x I decided to create an adaptive method of getting the pagesize. It will try different methods if the previous one fails. People can also force a specific mode of operation with the `-m` parameter. - -The order of operation is as follows: - 1. Try to get `/MediaBox` with `cat` + `grep` - 2. Failed AND MacOS ? Try `mdls` - 3. Failed AND NOT MacOS ? Try `pdfinfo` - 4. Failed ? Try ImageMagick's `identify` - 5. Failed ? `Exit` with error message - -The `cat`+`grep` method will fail on PDFs without a `/MediaBox`. - -You may install any of the above to be used in that case. - +#### Resize to A2 and disables Auto-Rotation +``` +$ pdfscale -v -r A2 -a none ../input.pdf +pdfscale v2.0.0 - Verbose Execution + Single Task: Resize PDF Paper + Input File: ../input.pdf + Output File: ../input.A2.pdf + Get Page Size: Adaptive Enabled + Method: Grep + Source Width: 595 postscript-points + Source Height: 842 postscript-points + Scale Factor: Disabled (resize only) + Auto Rotate: None + Flip Detect: No change needed + Run Resizing: A2 ( 1191 x 1684 ) pts + Final Status: File created successfully +``` +#### Resize to custom 200x200 mm, disable Flip-Detection and Scale by 0.95 (-5%) +``` +$ pdfscale -v -v -r 'custom mm 200 200' -f disable -s 0.9 ../mixsync\ manual\ v1-2-3.pdf +2017-05-15:03:06:23 | pdfscale v2.0.0 - Verbose Execution +2017-05-15:03:06:23 | Mixed Tasks: Resize & Scale +2017-05-15:03:06:23 | Input File: ../mixsync manual v1-2-3.pdf +2017-05-15:03:06:23 | Output File: ../mixsync manual v1-2-3.CUSTOM.SCALED.pdf +2017-05-15:03:06:23 | Get Page Size: Adaptive Enabled +2017-05-15:03:06:23 | Method: Grep +2017-05-15:03:06:23 | Source Width: 842 postscript-points +2017-05-15:03:06:23 | Source Height: 595 postscript-points +2017-05-15:03:06:23 | Auto Rotate: PageByPage +2017-05-15:03:06:23 | Flip Detect: Disabled +2017-05-15:03:06:23 | Run Resizing: CUSTOM ( 567 x 567 ) pts +2017-05-15:03:06:23 | New Width: 567 postscript-points +2017-05-15:03:06:23 | New Height: 567 postscript-points +2017-05-15:03:06:23 | Scale Factor: 0.9 +2017-05-15:03:06:23 | Translation X: 31.499685 +2017-05-15:03:06:23 | Translation Y: 31.499685 +2017-05-15:03:06:23 | Run Scaling: -10 % +2017-05-15:03:06:23 | Final Status: File created successfully +``` + ## Help info ``` -$ pdfscale -h -pdfscale v1.4.9 +$ ./pdfScale.sh -h +pdfScale.sh v2.0.0 -Usage: pdfscale [-v] [-s ] [-m ] [outfile.pdf] - pdfscale -h - pdfscale -V +Usage: pdfScale.sh + pdfScale.sh -i + pdfScale.sh [-v] [-s ] [-m ] [outfile.pdf] + pdfScale.sh [-v] [-r ] [-f ] [-a ] [outfile.pdf] + pdfScale.sh -p + pdfScale.sh -h + pdfScale.sh -V Parameters: -v Verbose mode, prints extra information - Use twice for even more information + Use twice for timestamp -h Print this help to screen and exits -V Prints version to screen and exits - -m Force a mode of page size detection + -m Page size Detection mode May disable the Adaptive Mode - -s Changes the scaling factor, defaults to 0.95 + -i Prints Page Size information to screen and exits + -s Changes the scaling factor or forces scaling + Defaults: 0.95 / no scaling (resize mode) MUST be a number bigger than zero - Eg. -s 0.8 for 80% of the original size + Eg. -s 0.8 for 80% of the original size + -r Triggers the Resize Paper Mode + Resize PDF paper proportionally + Uses a valid paper name or a custom defined paper + -f Flip Detection Mode, defaults to 'auto'. + Inverts Width <-> Height of a Resized PDF. + Modes: a, auto - automatic detection, default + f, force - forces flip W <-> H + d, disable - disables flipping + -a GS Auto-Rotation Setting, defaults to 'PageByPage'. + Setting for GS -dAutoRotatePages. + Modes: p, pagebypage - auto-rotates pages individually + a, all - rotates all pages (or none) depending + on a kind of "majority decision" + n, none - retains orientation of each page + -p Prints Ghostscript paper info tables to screen + +Scaling Mode: + The default mode of operation is scaling mode with fixed paper + size and scaling pre-set to 0.95. By not using the resize mode + you are using scaling mode. Flip-Detection and Auto-Rotation are + disabled in Scaling mode. + +Resize Paper Mode: + Disables the default scaling factor! (0.95) + Changes the PDF Paper Size in points. Will fit-to-page. + +Mixed Mode: + In mixed mode both the -s option and -r option must be specified. + The PDF will be first resized then scaled. + +Output filename: + The output filename is optional. If no file name is passed + the output file will have the same name/destination of the + input file with added suffixes: + .SCALED.pdf is added to scaled files + ..pdf is added to resized files + ..SCALED.pdf is added in mixed mode -Modes: +Page Size Detection Modes: a, adaptive Default mode, tries all the methods below - c, cat+grep Forces the use of the cat + grep method + g, grep Forces the use of grep method m, mdls Forces the use of MacOS Quartz mdls - p, pdfinfo Forces the use of Linux PdfInfo + p, pdfinfo Forces the use of PDFInfo i, identify Forces the use of ImageMagick's Identify -Notes: +Valid Paper Names: (case-insensitive) + A0 A1 A2 A3 A4 + A4SMALL A5 A6 A7 A8 + A9 A10 ISOB0 ISOB1 ISOB2 + ISOB3 ISOB4 ISOB5 ISOB6 C0 + C1 C2 C3 C4 C5 + C6 11X17 LEDGER LEGAL LETTER + LETTERSMALL ARCHE ARCHD ARCHC ARCHB + ARCHA JISB0 JISB1 JISB2 JISB3 + JISB4 JISB5 JISB6 FLSA FLSE + HALFLETTER HAGAKI + +Custom Paper Size: + Paper size can be set manually in Milimeters, Inches or Points. + Use: pdfScale.sh -r 'custom ' + Ex: pdfScale.sh -r 'custom mm 300 300' + Measurements can be: mm, inch, pts. + Custom paper definition MUST be quoted into a single parameter. + Actual size is applied in points (mms and inches are transformed). + +Additional Notes: - Adaptive Page size detection will try different modes until it gets a page size. You can force a mode with -m 'mode'. - Options must be passed before the file names to be parsed. - - The output filename is optional. If no file name is passed - the output file will have the same name/destination of the - input file, with .SCALED.pdf at the end (instead of just .pdf). - Having the extension .pdf on the output file name is optional, it will be added if not present. - - Should handle file names with spaces without problems. + - File and folder names with spaces should be quoted or escaped. - The scaling is centered and using a scale bigger than 1 may result on cropping parts of the pdf. + - Most of the options are case-insensitive, Ex: -m PdFinFo Examples: - pdfscale myPdfFile.pdf - pdfscale myPdfFile.pdf myScaledPdf - pdfscale -v -v myPdfFile.pdf - pdfscale -s 0.85 myPdfFile.pdf myScaledPdf.pdf - pdfscale -m pdfinfo -s 0.80 -v myPdfFile.pdf - pdfscale -v -v -m i -s 0.7 myPdfFile.pdf - pdfscale -h -``` - -## Example runs -``` -$ pdfscale -m i -v -s 0.9 ../input.pdf -pdfscale v1.4.9 - Verbose execution -Checking for ghostscript and bcmath -Checking for imagemagick's identify - Scale factor: 0.9 - Input file: ../input.pdf - Output file: ../input.SCALED.pdf - Adaptive mode: Disabled - Method: ImageMagick's Identify - Width: 595 postscript-points - Height: 842 postscript-points - Translation X: 33.055225 - Translation Y: 46.777310 -``` -``` -$ pdfscale -v -v -s 0.5 ../input-nup.pdf ../nuptest -2017-02-22:07:56:38 | pdfscale v1.4.9 - Verbose execution -2017-02-22:07:56:38 | Checking for ghostscript and bcmath -2017-02-22:07:56:38 | Scale factor: 0.5 -2017-02-22:07:56:38 | Input file: ../input-nup.pdf -2017-02-22:07:56:39 | Output file: ../nuptest.pdf -2017-02-22:07:56:39 | Adaptive mode: Enabled -2017-02-22:07:56:39 | Method: Cat + Grep -2017-02-22:07:56:39 | Failed -2017-02-22:07:56:39 | Method: Mac Quartz mdls -2017-02-22:07:56:39 | Width: 842 postscript-points -2017-02-22:07:56:39 | Height: 595 postscript-points -2017-02-22:07:56:39 | Translation X: 421.000000 -2017-02-22:07:56:39 | Translation Y: 297.500000 + pdfScale.sh myPdfFile.pdf + pdfScale.sh -i '/home/My Folder/My PDF File.pdf' + pdfScale.sh myPdfFile.pdf "My Scaled Pdf" + pdfScale.sh -v -v myPdfFile.pdf + pdfScale.sh -s 0.85 myPdfFile.pdf My\ Scaled\ Pdf.pdf + pdfScale.sh -m pdfinfo -s 0.80 -v myPdfFile.pdf + pdfScale.sh -v -v -m i -s 0.7 myPdfFile.pdf + pdfScale.sh -r A4 myPdfFile.pdf + pdfScale.sh -v -v -r "custom mm 252 356" -s 0.9 -f "../input file.pdf" "../my new pdf" +``` + +## GhostScript Paper Tables +The `-p` parameter prints detailed paper types information +``` +$ ./pdfScale.sh -p +pdfScale.sh v2.0.0 + +Valid Ghostscript Paper Sizes accepted + ++-----------------------------------------------------------------+ +| ISO STANDARD | ++-----------------------------------------------------------------+ +| Name | inchW | inchH | mm W | mm H | pts W | pts H | ++-----------------+-------+-------+-------+-------+-------+-------+ +| a0 | 33.1 | 46.8 | 841 | 1189 | 2384 | 3370 | +| a1 | 23.4 | 33.1 | 594 | 841 | 1684 | 2384 | +| a2 | 16.5 | 23.4 | 420 | 594 | 1191 | 1684 | +| a3 | 11.7 | 16.5 | 297 | 420 | 842 | 1191 | +| a4 | 8.3 | 11.7 | 210 | 297 | 595 | 842 | +| a4small | 8.3 | 11.7 | 210 | 297 | 595 | 842 | +| a5 | 5.8 | 8.3 | 148 | 210 | 420 | 595 | +| a6 | 4.1 | 5.8 | 105 | 148 | 297 | 420 | +| a7 | 2.9 | 4.1 | 74 | 105 | 210 | 297 | +| a8 | 2.1 | 2.9 | 52 | 74 | 148 | 210 | +| a9 | 1.5 | 2.1 | 37 | 52 | 105 | 148 | +| a10 | 1.0 | 1.5 | 26 | 37 | 73 | 105 | +| isob0 | 39.4 | 55.7 | 1000 | 1414 | 2835 | 4008 | +| isob1 | 27.8 | 39.4 | 707 | 1000 | 2004 | 2835 | +| isob2 | 19.7 | 27.8 | 500 | 707 | 1417 | 2004 | +| isob3 | 13.9 | 19.7 | 353 | 500 | 1001 | 1417 | +| isob4 | 9.8 | 13.9 | 250 | 353 | 709 | 1001 | +| isob5 | 6.9 | 9.8 | 176 | 250 | 499 | 709 | +| isob6 | 4.9 | 6.9 | 125 | 176 | 354 | 499 | +| c0 | 36.1 | 51.1 | 917 | 1297 | 2599 | 3677 | +| c1 | 25.5 | 36.1 | 648 | 917 | 1837 | 2599 | +| c2 | 18.0 | 25.5 | 458 | 648 | 1298 | 1837 | +| c3 | 12.8 | 18.0 | 324 | 458 | 918 | 1298 | +| c4 | 9.0 | 12.8 | 229 | 324 | 649 | 918 | +| c5 | 6.4 | 9.0 | 162 | 229 | 459 | 649 | +| c6 | 4.5 | 6.4 | 114 | 162 | 323 | 459 | ++-----------------+-------+-------+-------+-------+-------+-------+ + ++-----------------------------------------------------------------+ +| US STANDARD | ++-----------------------------------------------------------------+ +| Name | inchW | inchH | mm W | mm H | pts W | pts H | ++-----------------+-------+-------+-------+-------+-------+-------+ +| 11x17 | 11.0 | 17.0 | 279 | 432 | 792 | 1224 | +| ledger | 17.0 | 11.0 | 432 | 279 | 1224 | 792 | +| legal | 8.5 | 14.0 | 216 | 356 | 612 | 1008 | +| letter | 8.5 | 11.0 | 216 | 279 | 612 | 792 | +| lettersmall | 8.5 | 11.0 | 216 | 279 | 612 | 792 | +| archE | 36.0 | 48.0 | 914 | 1219 | 2592 | 3456 | +| archD | 24.0 | 36.0 | 610 | 914 | 1728 | 2592 | +| archC | 18.0 | 24.0 | 457 | 610 | 1296 | 1728 | +| archB | 12.0 | 18.0 | 305 | 457 | 864 | 1296 | +| archA | 9.0 | 12.0 | 229 | 305 | 648 | 864 | ++-----------------+-------+-------+-------+-------+-------+-------+ + ++-----------------------------------------------------------------+ +| JIS STANDARD *Aproximated Points | ++-----------------------------------------------------------------+ +| Name | inchW | inchH | mm W | mm H | pts W | pts H | ++-----------------+-------+-------+-------+-------+-------+-------+ +| jisb0 | NA | NA | 1030 | 1456 | 2920 | 4127 | +| jisb1 | NA | NA | 728 | 1030 | 2064 | 2920 | +| jisb2 | NA | NA | 515 | 728 | 1460 | 2064 | +| jisb3 | NA | NA | 364 | 515 | 1032 | 1460 | +| jisb4 | NA | NA | 257 | 364 | 729 | 1032 | +| jisb5 | NA | NA | 182 | 257 | 516 | 729 | +| jisb6 | NA | NA | 128 | 182 | 363 | 516 | ++-----------------+-------+-------+-------+-------+-------+-------+ + ++-----------------------------------------------------------------+ +| OTHERS | ++-----------------------------------------------------------------+ +| Name | inchW | inchH | mm W | mm H | pts W | pts H | ++-----------------+-------+-------+-------+-------+-------+-------+ +| flsa | 8.5 | 13.0 | 216 | 330 | 612 | 936 | +| flse | 8.5 | 13.0 | 216 | 330 | 612 | 936 | +| halfletter | 5.5 | 8.5 | 140 | 216 | 396 | 612 | +| hagaki | 3.9 | 5.8 | 100 | 148 | 283 | 420 | ++-----------------+-------+-------+-------+-------+-------+-------+ +``` + +## Dependencies +The script uses `basename`, `grep`, `bc` and `gs` (ghostscript). +You probably have everything installed already, except for ghostscript. +Optional dependencies are `imagemagick`, `pdfinfo` and `mdls` (Mac). +This app is focused in `Bash`, so it will probably not run in other shells. +The script will need to see the dependencies on your `$PATH` variable. + +##### apt-get +``` +sudo apt-get install ghostscript bc +``` +##### yum +``` +sudo yum install ghostscript bc +``` +##### homebrew MacOS +``` +brew install ghostscript +``` +##### Optionals +Page Size detection is by default in Adaptive Mode. +It will try the following methods in sequence: + 1. Try to get `/MediaBox` with `grep` (fastest) + 2. Failed AND MacOS ? Try `mdls` + 3. Failed ? Try `pdfinfo` + 4. Failed ? Try ImageMagick's `identify` + 5. Failed ? `Exit` with error message + +The `grep` method will fail on PDFs without a `/MediaBox`. +You may install any of the optionals to be used in that case. + +MacOS is fine using `mdls` if the metadata of the file is accurate. +The metadata is generated automatically by the OS (Spotlight) + +##### apt-get +``` +sudo apt-get install imagemagick pdfinfo +``` +##### yum +``` +sudo yum install imagemagick pdfinfo +``` +##### homebrew MacOS +``` +brew install imagemagick xpdf ``` ## System Install diff --git a/pdfScale.sh b/pdfScale.sh index 4627cd0..cad5d61 100755 --- a/pdfScale.sh +++ b/pdfScale.sh @@ -5,38 +5,26 @@ # Scale PDF to specified percentage of original size. # # Gustavo Arnosti Neves - 2016 / 07 / 10 +# Latest Version - 2017 / 05 / 14 # # This script: https://github.com/tavinus/pdfScale # Based on: http://ma.juii.net/blog/scale-page-content-of-pdf-files # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a -################################################### -# PAGESIZE LOGIC -# 1- Try to get Mediabox with CAT/GREP -# Remove /BBox search as it is unreliable -# 2- MacOS => try to use mdls -# Linux => try to use pdfinfo -# 3- Try to use identify (imagemagick) -# 4- Fail -# Remove postscript method, -# may have licensing problems -################################################### +VERSION="2.0.0" -VERSION="1.4.9" -SCALE="0.95" # scaling factor (0.95 = 95%, e.g.) -VERBOSE=0 # verbosity Level -BASENAME="$(basename $0)" # simplified name of this script +###################### EXTERNAL PROGRAMS ####################### -# Set with which after we check dependencies -GSBIN="" # GhostScript Binary -BCBIN="" # BC Math Binary -IDBIN="" # Identify Binary -PDFINFOBIN="" # PDF Info Binary -MDLSBIN="" # MacOS mdls Binary +GSBIN="" # GhostScript Binary +BCBIN="" # BC Math Binary +IDBIN="" # Identify Binary +PDFINFOBIN="" # PDF Info Binary +MDLSBIN="" # MacOS mdls Binary -OSNAME="$(uname 2>/dev/null)" # Check where we are running + +##################### ENVIRONMENT SET-UP ####################### LC_MEASUREMENT="C" # To make sure our numbers have .decimals LC_ALL="C" # Some languages use , as decimal token @@ -46,178 +34,472 @@ LC_NUMERIC="C" TRUE=0 # Silly stuff FALSE=1 -ADAPTIVEMODE=$TRUE # Automatically try to guess best mode -MODE="" - +########################### GLOBALS ############################ + +SCALE="0.95" # scaling factor (0.95 = 95%, e.g.) +VERBOSE=0 # verbosity Level +PDFSCALE_NAME="$(basename $0)" # simplified name of this script +OSNAME="$(uname 2>/dev/null)" # Check where we are running + +JUST_IDENTIFY=$FALSE # Flag to just show PDF info +ADAPTIVEMODE=$TRUE # Automatically try to guess best mode +AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, override by resize mode +MODE="" # Which page size detection to use +RESIZE_PAPER_TYPE="" # Pre-defined paper to use +CUSTOM_RESIZE_PAPER=$FALSE # If we are using a custom-defined paper +FLIP_DETECTION=$TRUE # If we shoudl run the Flip-detection +FLIP_FORCE=$FALSE # If we should force Flipping +AUTO_ROTATION='/PageByPage' # GS cal auto-rotation setting +PGWIDTH="" # Input PDF Page Width +PGHEIGHT="" # Input PDF Page Height +RESIZE_WIDTH="" # Resized PDF Page Width +RESIZE_HEIGHT="" # Resized PDF Page Height + + +########################## EXIT FLAGS ########################## + +EXIT_SUCCESS=0 +EXIT_ERROR=1 +EXIT_INVALID_PAGE_SIZE_DETECTED=10 +EXIT_FILE_NOT_FOUND=20 +EXIT_INPUT_NOT_PDF=21 +EXIT_INVALID_OPTION=22 +EXIT_NO_INPUT_FILE=23 +EXIT_INVALID_SCALE=24 +EXIT_MISSING_DEPENDENCY=25 +EXIT_IMAGEMAGIK_NOT_FOUND=26 +EXIT_MAC_MDLS_NOT_FOUND=27 +EXIT_PDFINFO_NOT_FOUND=28 +EXIT_TEMP_FILE_EXISTS=40 +EXIT_INVALID_PAPER_SIZE=50 + + +############################# MAIN ############################# + +# Main function called at the end +main() { + checkDeps + printPDFSizes + vprint " Input File: $INFILEPDF" + vprint " Output File: $OUTFILEPDF" + getPageSize + vPrintPageSizes ' Source' + local finalRet=$EXIT_ERROR + local tempFile="" + local tempSuffix="$RANDOM$RANDOM""_TEMP_$RANDOM$RANDOM.pdf" + + if isMixedMode; then + outputFile="$OUTFILEPDF" # backup outFile name + tempFile="${OUTFILEPDF%.pdf}.$tempSuffix" # set a temp file name + if isFile "$tempFile"; then + printError $'Error! Temporary file name already exists!\n'"File: $tempFile"$'\nAborting execution to avoid overwriting the file.\nPlease Try again...' + exit $EXIT_TEMP_FILE_EXISTS + fi + OUTFILEPDF="$tempFile" # set output to tmp file + pageResize # resize to tmp file + finalRet=$? + INFILEPDF="$tempFile" # get tmp file as input + OUTFILEPDF="$outputFile" # reset final target + PGWIDTH=$RESIZE_WIDTH # we already know the new page size + PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize) + vPrintPageSizes ' New' + vPrintScaleFactor + pageScale # scale the resized pdf + finalRet=$(($finalRet+$?)) + # remove tmp file + rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile" + elif isResizeMode; then + vPrintScaleFactor "Disabled (resize only)" + pageResize + finalRet=$? + else + local scaleMode="" + isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)' + vPrintScaleFactor "$SCALE $scaleMode" + pageScale + finalRet=$? + fi -# Prints version -printVersion() { - if [[ $1 -eq 2 ]]; then - echo >&2 "$BASENAME v$VERSION" + if [[ finalRet -eq $EXIT_SUCCESS ]]; then + vprint " Final Status: File created successfully" else - echo "$BASENAME v$VERSION" + vprint " Final Status: Errors were detected. Exit status: $finalRet" fi + + return $finalRet } +# Prints PDF Info and exits with $EXIT_SUCCESS, but only if $JUST_IDENTIFY is $TRUE +printPDFSizes() { + if [[ $JUST_IDENTIFY -eq $TRUE ]]; then + VERBOSE=0 + printVersion 3 " - Paper Sizes" + getPageSize || initError "Could not get pagesize!" + local paperType="$(getGSPaperName $PGWIDTH $PGHEIGHT)" + isEmpty "$paperType" && paperType="NOT Detected" + printf '%s\n' "------------+-----------------------------" + printf " File | %s\n" "$(basename "$INFILEPDF")" + printf " Paper Type | %s\n" "$paperType" + printf '%s\n' "------------+-----------------------------" + printf '%s\n' " | WIDTH x HEIGHT" + printf " Points | %+8s x %-8s\n" "$PGWIDTH" "$PGHEIGHT" + printf " Milimeters | %+8s x %-8s\n" "$(pointsToMilimeters $PGWIDTH)" "$(pointsToMilimeters $PGHEIGHT)" + printf " Inches | %+8s x %-8s\n" "$(pointsToInches $PGWIDTH)" "$(pointsToInches $PGHEIGHT)" + exit $EXIT_SUCCESS + fi + return $EXIT_SUCCESS +} -# Prints help info -printHelp() { - printVersion - echo " -Usage: $BASENAME [-v] [-s ] [-m ] [outfile.pdf] - $BASENAME -h - $BASENAME -V +###################### GHOSTSCRIPT CALLS ####################### -Parameters: - -v Verbose mode, prints extra information - Use twice for even more information - -h Print this help to screen and exits - -V Prints version to screen and exits - -m Force a mode of page size detection - May disable the Adaptive Mode - -s Changes the scaling factor, defaults to 0.95 - MUST be a number bigger than zero - Eg. -s 0.8 for 80% of the original size +# Runs the ghostscript scaling script +pageScale() { + # Compute translation factors (to center page). + XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN") + YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN") + vprint " Translation X: $XTRANS" + vprint " Translation Y: $YTRANS" -Modes: - a, adaptive Default mode, tries all the methods below - c, cat+grep Forces the use of the cat + grep method - m, mdls Forces the use of MacOS Quartz mdls - p, pdfinfo Forces the use of Linux PdfInfo - i, identify Forces the use of ImageMagick's Identify + local increase=$(echo "scale=0; (($SCALE - 1) * 100)/1" | "$BCBIN") + vprint " Run Scaling: $increase %" -Notes: - - Adaptive Page size detection will try different modes until - it gets a page size. You can force a mode with -m 'mode'. - - Options must be passed before the file names to be parsed. - - The output filename is optional. If no file name is passed - the output file will have the same name/destination of the - input file, with .SCALED.pdf at the end (instead of just .pdf). - - Having the extension .pdf on the output file name is optional, - it will be added if not present. - - Should handle file names with spaces without problems. - - The scaling is centered and using a scale bigger than 1 may - result on cropping parts of the pdf. + # Scale page + "$GSBIN" \ +-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \ +-dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \ +-dColorConversionStrategy=/LeaveColorUnchanged \ +-dSubsetFonts=true -dEmbedAllFonts=true \ +-dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \ +-sOutputFile="$OUTFILEPDF" \ +-c "<> setpagedevice" \ +-f "$INFILEPDF" -Examples: - $BASENAME myPdfFile.pdf - $BASENAME myPdfFile.pdf myScaledPdf - $BASENAME -v -v myPdfFile.pdf - $BASENAME -s 0.85 myPdfFile.pdf myScaledPdf.pdf - $BASENAME -m pdfinfo -s 0.80 -v myPdfFile.pdf - $BASENAME -v -v -m i -s 0.7 myPdfFile.pdf - $BASENAME -h -" + return $? } -# Prints usage info -usage() { - printVersion 2 - echo >&2 "Usage: $BASENAME [-v] [-s ] [-m ] [outfile.pdf]" - echo >&2 "Try: $BASENAME -h # for help" - exit 1 +# Runs the ghostscript paper resize script +pageResize() { + # Get new paper sizes if not custom paper + isNotCustomPaper && getGSPaperSize "$RESIZE_PAPER_TYPE" + + vprint " Auto Rotate: $(basename $AUTO_ROTATION)" + + # Flip detect + local tmpInverter="" + if [[ $FLIP_DETECTION -eq $TRUE || $FLIP_FORCE -eq $TRUE ]]; then + if [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]] || [[ $FLIP_FORCE -eq $TRUE ]]; then + [[ $FLIP_FORCE -eq $TRUE ]] && vprint " Flip Detect: Forced Mode!" || vprint " Flip Detect: Wrong orientation detected!" + vprint " Inverting Width <-> Height" + tmpInverter=$RESIZE_HEIGHT + RESIZE_HEIGHT=$RESIZE_WIDTH + RESIZE_WIDTH=$tmpInverter + else + vprint " Flip Detect: No change needed" + fi + else + vprint " Flip Detect: Disabled" + fi + + vprint " Run Resizing: $(uppercase "$RESIZE_PAPER_TYPE") ( "$RESIZE_WIDTH" x "$RESIZE_HEIGHT" ) pts" + + # Change page size + "$GSBIN" \ +-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \ +-dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \ +-dColorConversionStrategy=/LeaveColorUnchanged \ +-dSubsetFonts=true -dEmbedAllFonts=true \ +-dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \ +-dAutoRotatePages=$AUTO_ROTATION \ +-dFIXEDMEDIA -dPDFFitPage \ +-sOutputFile="$OUTFILEPDF" \ +-f "$INFILEPDF" + + return $? } -# Prints Verbose information -vprint() { - [[ $VERBOSE -eq 0 ]] && return 0 - timestamp="" - [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | " - echo "$timestamp$1" +########################## INITIALIZERS ######################### + +# Loads external dependencies and checks for errors +initDeps() { + GREPBIN="$(which grep 2>/dev/null)" + GSBIN="$(which gs 2>/dev/null)" + BCBIN="$(which bc 2>/dev/null)" + IDBIN=$(which identify 2>/dev/null) + MDLSBIN="$(which mdls 2>/dev/null)" + PDFINFOBIN="$(which pdfinfo 2>/dev/null)" + + vprint "Checking for basename, grep, ghostscript and bcmath" + basename "" >/dev/null 2>&1 || printDependency 'basename' + notIsAvailable "$GREPBIN" && printDependency 'grep' + notIsAvailable "$GSBIN" && printDependency 'ghostscript' + notIsAvailable "$BCBIN" && printDependency 'bc' + return $TRUE } +# Checks for dependencies errors, run after getting options +checkDeps() { + if [[ $MODE = "IDENTIFY" ]]; then + vprint "Checking for imagemagick's identify" + if notIsAvailable "$IDBIN"; then printDependency 'imagemagick'; fi + fi + if [[ $MODE = "PDFINFO" ]]; then + vprint "Checking for pdfinfo" + if notIsAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi + fi + if [[ $MODE = "MDLS" ]]; then + vprint "Checking for MacOS mdls" + if notIsAvailable "$MDLSBIN"; then + initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND 'nobanner' + fi + fi + return $TRUE +} -# Prints dependency information and aborts execution -printDependency() { - printVersion 2 - echo >&2 $'\n'"ERROR! You need to install the package '$1'"$'\n' - echo >&2 "Linux apt-get.: sudo apt-get install $1" - echo >&2 "Linux yum.....: sudo yum install $1" - echo >&2 "MacOS homebrew: brew install $1" - echo >&2 $'\n'"Aborting..." - exit 3 + +######################### CLI OPTIONS ########################## + +# Parse options +getOptions() { + while getopts ":vhVis:m:r:pf:a:" o; do + case "${o}" in + v) + ((VERBOSE++)) + ;; + h) + printHelp + exit $EXIT_SUCCESS + ;; + V) + printVersion + exit $EXIT_SUCCESS + ;; + i) + JUST_IDENTIFY=$TRUE + ;; + s) + parseScale ${OPTARG} + ;; + m) + parseMode ${OPTARG} + ;; + r) + parsePaperResize ${OPTARG} + ;; + p) + printPaperInfo + exit $EXIT_SUCCESS + ;; + f) + parseFlipDetectionMode ${OPTARG} + ;; + a) + parseAutoRotationMode ${OPTARG} + ;; + *) + initError "Invalid Option: -$OPTARG" $EXIT_INVALID_OPTION + ;; + esac + done + shift $((OPTIND-1)) + + if [[ $JUST_IDENTIFY -eq $TRUE ]]; then + VERBOSE=0 + fi + + # Validate input PDF file + INFILEPDF="$1" + isEmpty "$INFILEPDF" && initError "Input file is empty!" $EXIT_NO_INPUT_FILE + isPDF "$INFILEPDF" || initError "Input file is not a PDF file: $INFILEPDF" $EXIT_INPUT_NOT_PDF + isFile "$INFILEPDF" || initError "Input file not found: $INFILEPDF" $EXIT_FILE_NOT_FOUND + + printVersion 1 'verbose' + if isMixedMode; then + vprint " Mixed Tasks: Resize & Scale" + isEmpty "$2" && OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf" + elif isResizeMode; then + vprint " Single Task: Resize PDF Paper" + isEmpty "$2" && OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).pdf" + else + vprint " Single Task: Scale PDF Contents" + isEmpty "$2" && OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf" + fi + isNotEmpty "$2" && OUTFILEPDF="${2%.pdf}.pdf" } # Parses and validates the scaling factor parseScale() { - if ! [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ && (($1 > 0 )) ]] ; then - echo >&2 "Invalid factor: $1" - echo >&2 "The factor must be a floating point number greater than 0" - echo >&2 "Example: for 80% use 0.8" - exit 2 + AUTOMATIC_SCALING=$FALSE + if ! isFloatBiggerThanZero "$1"; then + printError "Invalid factor: $1" + printError "The factor must be a floating point number greater than 0" + printError "Example: for 80% use 0.8" + exit $EXIT_INVALID_SCALE fi - SCALE=$1 + SCALE="$1" } - # Parse a forced mode of operation parseMode() { - if [[ -z $1 ]]; then - echo "Mode is empty, please specify the desired mode" - echo "Falling back to adaptive mode!" - ADAPTIVEMODE=$TRUE - MODE="" - return $FALSE - fi + local param="$(lowercase $1)" + case "${param}" in + c|catgrep|'cat+grep'|grep|g) + ADAPTIVEMODE=$FALSE + MODE="CATGREP" + return $TRUE + ;; + i|imagemagick|identify) + ADAPTIVEMODE=$FALSE + MODE="IDENTIFY" + return $TRUE + ;; + m|mdls|quartz|mac) + ADAPTIVEMODE=$FALSE + MODE="MDLS" + return $TRUE + ;; + p|pdfinfo) + ADAPTIVEMODE=$FALSE + MODE="PDFINFO" + return $TRUE + ;; + *) + ADAPTIVEMODE=$TRUE + MODE="" + if [[ "$param" != 'a' && "$param" != 'auto' && "$param" != 'automatic' && "$param" != 'adaptive' ]]; then + printError "Error! Invalid PDF Size Detection Mode: \"$1\", using adaptive mode!" + return $FALSE + fi + return $TRUE + ;; + esac - if [[ $1 = 'c' || $1 = 'catgrep' || $1 = 'cat+grep' || $1 = 'CatGrep' || $1 = 'C' || $1 = 'CATGREP' ]]; then - ADAPTIVEMODE=$FALSE - MODE="CATGREP" - return $TRUE - elif [[ $1 = 'i' || $1 = 'imagemagick' || $1 = 'identify' || $1 = 'ImageMagick' || $1 = 'Identify' || $1 = 'I' || $1 = 'IDENTIFY' ]]; then - ADAPTIVEMODE=$FALSE - MODE="IDENTIFY" - return $TRUE - elif [[ $1 = 'm' || $1 = 'mdls' || $1 = 'MDLS' || $1 = 'quartz' || $1 = 'mac' || $1 = 'M' ]]; then - ADAPTIVEMODE=$FALSE - MODE="MDLS" - return $TRUE - elif [[ $1 = 'p' || $1 = 'pdfinfo' || $1 = 'PDFINFO' || $1 = 'PdfInfo' || $1 = 'P' ]]; then - ADAPTIVEMODE=$FALSE - MODE="PDFINFO" - return $TRUE - elif [[ $1 = 'a' || $1 = 'adaptive' || $1 = 'automatic' || $1 = 'A' || $1 = 'ADAPTIVE' || $1 = 'AUTOMATIC' ]]; then - ADAPTIVEMODE=$TRUE - MODE="" + return $FALSE +} + +# Parses and validates the scaling factor +parseFlipDetectionMode() { + local param="$(lowercase $1)" + case "${param}" in + d|disable) + FLIP_DETECTION=$FALSE + FLIP_FORCE=$FALSE + ;; + f|force) + FLIP_DETECTION=$FALSE + FLIP_FORCE=$TRUE + ;; + *) + [[ "$param" != 'a' && "$param" != 'auto' ]] && printError "Error! Invalid Flip Detection Mode: \"$1\", using automatic mode!" + FLIP_DETECTION=$TRUE + FLIP_FORCE=$FALSE + ;; + esac +} + +# Parses and validates the scaling factor +parseAutoRotationMode() { + local param="$(lowercase $1)" + case "${param}" in + n|none) + AUTO_ROTATION='/None' + ;; + a|all) + AUTO_ROTATION='/All' + ;; + p|pagebypage|auto) + AUTO_ROTATION='/PageByPage' + ;; + *) + printError "Error! Invalid Auto Rotation Mode: $param, using default: $(basename $AUTO_ROTATION)" + ;; + esac +} + + +################### PDF PAGE SIZE DETECTION #################### + +################################################################ +# Detects operation mode and also runs the adaptive mode +# PAGESIZE LOGIC +# 1- Try to get Mediabox with GREP +# 2- MacOS => try to use mdls +# 3- Try to use pdfinfo +# 4- Try to use identify (imagemagick) +# 5- Fail +################################################################ +getPageSize() { + if isNotAdaptiveMode; then + vprint " Get Page Size: Adaptive Disabled" + if [[ $MODE = "CATGREP" ]]; then + vprint " Method: Grep" + getPageSizeCatGrep + elif [[ $MODE = "MDLS" ]]; then + vprint " Method: Mac Quartz mdls" + getPageSizeMdls + elif [[ $MODE = "PDFINFO" ]]; then + vprint " Method: PDFInfo" + getPageSizePdfInfo + elif [[ $MODE = "IDENTIFY" ]]; then + vprint " Method: ImageMagick's Identify" + getPageSizeImagemagick + else + printError "Error! Invalid Mode: $MODE" + printError "Aborting execution..." + exit $EXIT_INVALID_OPTION + fi return $TRUE - else - echo "Invalid mode: $1" - echo "Falling back to adaptive mode!" - ADAPTIVEMODE=$TRUE - MODE="" - return $FALSE fi - return $FALSE -} + vprint " Get Page Size: Adaptive Enabled" + vprint " Method: Grep" + getPageSizeCatGrep + if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then + vprint " Failed" + vprint " Method: Mac Quartz mdls" + getPageSizeMdls + fi + + if pageSizeIsInvalid; then + vprint " Failed" + vprint " Method: PDFInfo" + getPageSizePdfInfo + fi + + if pageSizeIsInvalid; then + vprint " Failed" + vprint " Method: ImageMagick's Identify" + getPageSizeImagemagick + fi + + if pageSizeIsInvalid; then + vprint " Failed" + printError "Error when detecting PDF paper size!" + printError "All methods of detection failed" + printError "You may want to install pdfinfo or imagemagick" + exit $EXIT_INVALID_PAGE_SIZE_DETECTED + fi + return $TRUE +} # Gets page size using imagemagick's identify getPageSizeImagemagick() { - # Sanity - if [[ ! -f $IDBIN && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error! ImageMagick's Identify was not found!" - echo "Make sure you installed ImageMagick and have identify on your \$PATH" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ ! -f $IDBIN && $ADAPTIVEMODE = $TRUE ]]; then + # Sanity and Adaptive together + if notIsFile "$IDBIN" && isNotAdaptiveMode; then + notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify" + elif notIsFile "$IDBIN" && isAdaptiveMode; then return $FALSE fi - + # get data from image magick local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)" - # No page size data available - if [[ -z $identify && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error when reading input file!" - echo "Could not determine the page size!" - echo "ImageMagicks's Identify returned an empty string!" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ -z $identify && $ADAPTIVEMODE = $TRUE ]]; then + if isEmpty "$identify" && isNotAdaptiveMode; then + notAdaptiveFailed "ImageMagicks's Identify returned an empty string!" + elif isEmpty "$identify" && isAdaptiveMode; then return $FALSE fi @@ -225,65 +507,59 @@ getPageSizeImagemagick() { identify=($identify) # make it an array PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign + + return $TRUE } # Gets page size using Mac Quarts mdls getPageSizeMdls() { - # Sanity - if [[ ! -f $MDLSBIN && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error! Mac Quartz mdls was not found!" - echo "Are you even trying this on a Mac?" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ ! -f $MDLSBIN && $ADAPTIVEMODE = $TRUE ]]; then + # Sanity and Adaptive together + if notIsFile "$MDLSBIN" && isNotAdaptiveMode; then + notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls" + elif notIsFile "$MDLSBIN" && isAdaptiveMode; then return $FALSE fi - - # get data from mdls + local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)" - - if [[ -z $identify && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error when reading input file!" - echo "Could not determine the page size!" - echo "Mac Quartz mdls returned an empty string!" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ -z $identify && $ADAPTIVEMODE = $TRUE ]]; then + + if isEmpty "$identify" && isNotAdaptiveMode; then + notAdaptiveFailed "Mac Quartz mdls returned an empty string!" + elif isEmpty "$identify" && isAdaptiveMode; then return $FALSE fi - identify=${identify//$'\t'/ } # change tab to space identify=($identify) # make it an array - + + if [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isNotAdaptiveMode; then + notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?" + elif [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isAdaptiveMode; then + return $FALSE + fi + PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign + + return $TRUE } # Gets page size using Linux PdfInfo getPageSizePdfInfo() { - # Sanity - if [[ ! -f $PDFINFOBIN && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error! Linux pdfinfo was not found!" - echo "Do you have pdfinfo installed and available on your \$PATH?" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ ! -f $PDFINFOBIN && $ADAPTIVEMODE = $TRUE ]]; then + # Sanity and Adaptive together + if notIsFile "$PDFINFOBIN" && isNotAdaptiveMode; then + notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo" + elif notIsFile "$PDFINFOBIN" && isAdaptiveMode; then return $FALSE fi - + # get data from image magick - local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | grep -i 'Page size:' )" - - if [[ -z $identify && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error when reading input file!" - echo "Could not determine the page size!" - echo "Linux PdfInfo returned an empty string!" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ -z $identify && $ADAPTIVEMODE = $TRUE ]]; then + local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | "$GREPBIN" -i 'Page size:' )" + + if isEmpty "$identify" && isNotAdaptiveMode; then + notAdaptiveFailed "Linux PdfInfo returned an empty string!" + elif isEmpty "$identify" && isAdaptiveMode; then return $FALSE fi @@ -292,6 +568,8 @@ getPageSizePdfInfo() { PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign + + return $TRUE } @@ -303,17 +581,20 @@ getPageSizeCatGrep() { # /MediaBox[ 0 0 595.28 841.89 ] # Get MediaBox data if possible - local mediaBox="$(cat "$INFILEPDF" | grep -a '/MediaBox' | head -n1)" + #local mediaBox="$(cat "$INFILEPDF" | "$GREPBIN" -a '/MediaBox' | "$HEADBIN" -n1)" + #local mediaBox="$("$GREPBIN" -a -e '/MediaBox' "$INFILEPDF" | "$HEADBIN" -n1)" + local mediaBox="$("$GREPBIN" -a -e '/MediaBox' "$INFILEPDF" 2>/dev/null)"$'\n\n' + while read l; do + mediaBox="$l" + break + done <<< "$mediaBox" + mediaBox="${mediaBox##*/MediaBox}" # No page size data available - if [[ -z $mediaBox && $ADAPTIVEMODE = $FALSE ]]; then - echo "Error when reading input file!" - echo "Could not determine the page size!" - echo "There is no MediaBox in the pdf document!" - echo "Aborting! You may want to try the adaptive mode." - exit 15 - elif [[ -z $mediaBox && $ADAPTIVEMODE = $TRUE ]]; then + if isEmpty "$mediaBox" && isNotAdaptiveMode; then + notAdaptiveFailed "There is no MediaBox in the pdf document!" + elif isEmpty "$mediaBox" && isAdaptiveMode; then return $FALSE fi @@ -326,9 +607,9 @@ getPageSizeCatGrep() { # sanity if [[ $mbCount -lt 4 ]]; then - echo "Error when reading the page size!" - echo "The page size information is invalid!" - exit 16 + printError "Error when reading the page size!" + printError "The page size information is invalid!" + exit $EXIT_INVALID_PAGE_SIZE_DETECTED fi # we are done @@ -338,161 +619,665 @@ getPageSizeCatGrep() { return $TRUE } +# Prints error message and exits execution +notAdaptiveFailed() { + local errProgram="$2" + local errStr="$1" + if isEmpty "$2"; then + printError "Error when reading input file!" + printError "Could not determine the page size!" + else + printError "Error! $2 was not found!" + fi + isNotEmpty "$errStr" && printError "$errStr" + printError "Aborting! You may want to try the adaptive mode." + exit $EXIT_INVALID_PAGE_SIZE_DETECTED +} -# Detects operation mode and also runs the adaptive mode -getPageSize() { - if [[ $ADAPTIVEMODE = $FALSE ]]; then - vprint " Adaptive mode: Disabled" - if [[ $MODE = "CATGREP" ]]; then - vprint " Method: Cat + Grep" - getPageSizeCatGrep - elif [[ $MODE = "MDLS" ]]; then - vprint " Method: Mac Quartz mdls" - getPageSizeMdls - elif [[ $MODE = "PDFINFO" ]]; then - vprint " Method: Linux PdfInfo" - getPageSizePdfInfo - elif [[ $MODE = "IDENTIFY" ]]; then - vprint " Method: ImageMagick's Identify" - getPageSizeImagemagick +# Verbose print of the Width and Height (Source or New) to screen +vPrintPageSizes() { + vprint " $1 Width: $PGWIDTH postscript-points" + vprint "$1 Height: $PGHEIGHT postscript-points" +} + + +#################### GHOSTSCRIPT PAPER INFO #################### + +# Loads GS paper info to memory +getPaperInfo() { + # name inchesW inchesH mmW mmH pointsW pointsH + sizesUS="\ +11x17 11.0 17.0 279 432 792 1224 +ledger 17.0 11.0 432 279 1224 792 +legal 8.5 14.0 216 356 612 1008 +letter 8.5 11.0 216 279 612 792 +lettersmall 8.5 11.0 216 279 612 792 +archE 36.0 48.0 914 1219 2592 3456 +archD 24.0 36.0 610 914 1728 2592 +archC 18.0 24.0 457 610 1296 1728 +archB 12.0 18.0 305 457 864 1296 +archA 9.0 12.0 229 305 648 864" + + sizesISO="\ +a0 33.1 46.8 841 1189 2384 3370 +a1 23.4 33.1 594 841 1684 2384 +a2 16.5 23.4 420 594 1191 1684 +a3 11.7 16.5 297 420 842 1191 +a4 8.3 11.7 210 297 595 842 +a4small 8.3 11.7 210 297 595 842 +a5 5.8 8.3 148 210 420 595 +a6 4.1 5.8 105 148 297 420 +a7 2.9 4.1 74 105 210 297 +a8 2.1 2.9 52 74 148 210 +a9 1.5 2.1 37 52 105 148 +a10 1.0 1.5 26 37 73 105 +isob0 39.4 55.7 1000 1414 2835 4008 +isob1 27.8 39.4 707 1000 2004 2835 +isob2 19.7 27.8 500 707 1417 2004 +isob3 13.9 19.7 353 500 1001 1417 +isob4 9.8 13.9 250 353 709 1001 +isob5 6.9 9.8 176 250 499 709 +isob6 4.9 6.9 125 176 354 499 +c0 36.1 51.1 917 1297 2599 3677 +c1 25.5 36.1 648 917 1837 2599 +c2 18.0 25.5 458 648 1298 1837 +c3 12.8 18.0 324 458 918 1298 +c4 9.0 12.8 229 324 649 918 +c5 6.4 9.0 162 229 459 649 +c6 4.5 6.4 114 162 323 459" + + sizesJIS="\ +jisb0 NA NA 1030 1456 2920 4127 +jisb1 NA NA 728 1030 2064 2920 +jisb2 NA NA 515 728 1460 2064 +jisb3 NA NA 364 515 1032 1460 +jisb4 NA NA 257 364 729 1032 +jisb5 NA NA 182 257 516 729 +jisb6 NA NA 128 182 363 516" + + sizesOther="\ +flsa 8.5 13.0 216 330 612 936 +flse 8.5 13.0 216 330 612 936 +halfletter 5.5 8.5 140 216 396 612 +hagaki 3.9 5.8 100 148 283 420" + + sizesAll="\ +$sizesUS +$sizesISO +$sizesJIS +$sizesOther" + +} + +# Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT +getGSPaperSize() { + isEmpty "$sizesall" && getPaperInfo + while read l; do + local cols=($l) + if [[ "$1" == ${cols[0]} ]]; then + RESIZE_WIDTH=${cols[5]} + RESIZE_HEIGHT=${cols[6]} + return $TRUE + fi + done <<< "$sizesAll" +} + +# Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT +getGSPaperName() { + local w="$(printf "%.0f" $1)" + local h="$(printf "%.0f" $2)" + isEmpty "$sizesall" && getPaperInfo + # Because US Standard has inverted sizes, I need to scan 2 times + # instead of just testing if width is bigger than height + while read l; do + local cols=($l) + if [[ "$w" == ${cols[5]} && "$h" == ${cols[6]} ]]; then + printf "%s Portrait" $(uppercase ${cols[0]}) + return $TRUE + fi + done <<< "$sizesAll" + while read l; do + local cols=($l) + if [[ "$w" == ${cols[6]} && "$h" == ${cols[5]} ]]; then + printf "%s Landscape" $(uppercase ${cols[0]}) + return $TRUE + fi + done <<< "$sizesAll" + return $FALSE +} + + +# Loads an array with paper names to memory +getPaperNames() { + paperNames=(a0 a1 a2 a3 a4 a4small a5 a6 a7 a8 a9 a10 isob0 isob1 isob2 isob3 isob4 isob5 isob6 c0 c1 c2 c3 c4 c5 c6 \ +11x17 ledger legal letter lettersmall archE archD archC archB archA \ +jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \ +flsa flse halfletter hagaki) +} + +# Prints uppercase paper names to screen (used in help) +printPaperNames() { + isEmpty "$paperNames" && getPaperNames + for i in "${!paperNames[@]}"; do + [[ $i -eq 0 ]] && echo -n -e ' ' + [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo -n -e $'\n ' + ppN="$(uppercase ${paperNames[i]})" + printf "%-14s" "$ppN" + done + echo "" +} + +# Returns $TRUE if $! is a valid paper name, $FALSE otherwise +isPaperName() { + isEmpty "$1" && return $FALSE + isEmpty "$paperNames" && getPaperNames + for i in "${paperNames[@]}"; do + [[ "$i" = "$1" ]] && return $TRUE + done + return $FALSE +} + +# Prints all tables with ghostscript paper information +printPaperInfo() { + printVersion + echo $'\n'"Valid Ghostscript Paper Sizes accepted"$'\n' + getPaperInfo + printPaperTable "ISO STANDARD" "$sizesISO"; echo + printPaperTable "US STANDARD" "$sizesUS"; echo + printPaperTable "JIS STANDARD *Aproximated Points" "$sizesJIS"; echo + printPaperTable "OTHERS" "$sizesOther"; echo +} + +# GS paper table helper, prints a full line +printTableLine() { + echo '+-----------------------------------------------------------------+' +} + +# GS paper table helper, prints a line with dividers +printTableDivider() { + echo '+-----------------+-------+-------+-------+-------+-------+-------+' +} + +# GS paper table helper, prints a table header +printTableHeader() { + echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |' +} + +# GS paper table helper, prints a table title +printTableTitle() { + printf "| %-64s%s\n" "$1" '|' +} + +# GS paper table printer, prints a table for a paper variable +printPaperTable() { + printTableLine + printTableTitle "$1" + printTableLine + printTableHeader + printTableDivider + while read l; do + local cols=($l) + printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]}; + done <<< "$2" + printTableDivider +} + +# Validades the a paper resize CLI option and sets the paper to $RESIZE_PAPER_TYPE +parsePaperResize() { + isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE + local lowercasePaper="$(lowercase $1)" + if [[ "$1" = 'custom' ]]; then + if isNotValidMeasure "$2" || ! isFloatBiggerThanZero "$3" || ! isFloatBiggerThanZero "$4"; then + initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom '"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION + fi + RESIZE_PAPER_TYPE="custom" + CUSTOM_RESIZE_PAPER=$TRUE + if isMilimeter "$2"; then + RESIZE_WIDTH="$(milimetersToPoints "$3")" + RESIZE_HEIGHT="$(milimetersToPoints "$4")" + elif isInch "$2"; then + RESIZE_WIDTH="$(inchesToPoints "$3")" + RESIZE_HEIGHT="$(inchesToPoints "$4")" + elif isPoint "$2"; then + RESIZE_WIDTH="$3" + RESIZE_HEIGHT="$4" else - echo "Error! Invalid Mode: $MODE" - echo "Aborting execution..." - exit 20 + initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom '"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION fi + else + isPaperName "$lowercasePaper" || initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE + RESIZE_PAPER_TYPE="$lowercasePaper" + fi +} + +# Returns $TRUE if $1 is a valid measurement for a custom paper, $FALSE otherwise +isNotValidMeasure() { + isMilimeter "$1" || isInch "$1" || isPoint "$1" && return $FALSE + return $TRUE +} + +# Returns $TRUE if $1 is a valid milimeter string, $FALSE otherwise +isMilimeter() { + [[ "$1" = 'mm' || "$1" = 'milimeters' || "$1" = 'milimeter' ]] && return $TRUE + return $FALSE +} + +# Returns $TRUE if $1 is a valid inch string, $FALSE otherwise +isInch() { + [[ "$1" = 'in' || "$1" = 'inch' || "$1" = 'inches' ]] && return $TRUE + return $FALSE +} + +# Returns $TRUE if $1 is a valid point string, $FALSE otherwise +isPoint() { + [[ "$1" = 'pt' || "$1" = 'pts' || "$1" = 'point' || "$1" = 'points' ]] && return $TRUE + return $FALSE +} + +# Returns $TRUE if a custom paper is being used, $FALSE otherwise +isCustomPaper() { + return $CUSTOM_RESIZE_PAPER +} + +isNotCustomPaper() { + isCustomPaper && return $FALSE + return $TRUE +} + +# Returns $TRUE if the scale was set manually, $FALSE if we are using automatic scaling +isManualScaledMode() { + [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE + return $TRUE +} + +# Returns true if we are resizing a paper (ignores scaling), false otherwise +isResizeMode() { + isEmpty $RESIZE_PAPER_TYPE && return $FALSE + return $TRUE +} + +# Returns true if we are resizing a paper and the scale was manually set +isMixedMode() { + isResizeMode && isManualScaledMode && return $TRUE + return $FALSE +} + +# Prints the lowercase char value for $1 +lowercaseChar() { + case "$1" in + [A-Z]) + n=$(printf "%d" "'$1") + n=$((n+32)) + printf \\$(printf "%o" "$n") + ;; + *) + printf "%s" "$1" + ;; + esac +} + +# Prints the lowercase version of a string +lowercase() { + word="$@" + for((i=0;i<${#word};i++)) + do + ch="${word:$i:1}" + lowercaseChar "$ch" + done +} + +# Prints the uppercase char value for $1 +uppercaseChar(){ + case "$1" in + [a-z]) + n=$(printf "%d" "'$1") + n=$((n-32)) + printf \\$(printf "%o" "$n") + ;; + *) + printf "%s" "$1" + ;; + esac +} + +# Prints the uppercase version of a string +uppercase() { + word="$@" + for((i=0;i<${#word};i++)) + do + ch="${word:$i:1}" + uppercaseChar "$ch" + done +} + +# Prints the postscript points rounded equivalent from $1 mm +milimetersToPoints() { + local pts=$(echo "scale=8; $1 * 72 / 25.4" | "$BCBIN") + printf '%.0f' "$pts" # Print rounded conversion +} + +# Prints the postscript points rounded equivalent from $1 inches +inchesToPoints() { + local pts=$(echo "scale=8; $1 * 72" | "$BCBIN") + printf '%.0f' "$pts" # Print rounded conversion +} + +# Prints the mm equivalent from $1 postscript points +pointsToMilimeters() { + local pts=$(echo "scale=8; $1 / 72 * 25.4" | "$BCBIN") + printf '%.0f' "$pts" # Print rounded conversion +} + +# Prints the inches equivalent from $1 postscript points +pointsToInches() { + local pts=$(echo "scale=8; $1 / 72" | "$BCBIN") + printf '%.1f' "$pts" # Print rounded conversion +} + + +########################## VALIDATORS ########################## + +# Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, $FALSE otherwise +pageSizeIsInvalid() { + if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then return $TRUE fi - - vprint " Adaptive mode: Enabled" - vprint " Method: Cat + Grep" - getPageSizeCatGrep - if [[ -z $PGWIDTH && -z $PGHEIGHT ]]; then - vprint " Failed" - if [[ $OSNAME = "Darwin" ]]; then - vprint " Method: Mac Quartz mdls" - getPageSizeMdls - else - vprint " Method: Linux PdfInfo" - getPageSizePdfInfo - fi + return $FALSE +} + + +# Return $TRUE if adaptive mode is enabled, $FALSE otherwise +isAdaptiveMode() { + return $ADAPTIVEMODE +} + + +# Return $TRUE if adaptive mode is disabled, $FALSE otherwise +isNotAdaptiveMode() { + isAdaptiveMode && return $FALSE + return $TRUE +} + + +# Return $TRUE if $1 is empty, $FALSE otherwise +isEmpty() { + [[ -z "$1" ]] && return $TRUE + return $FALSE +} + + +# Return $TRUE if $1 is NOT empty, $FALSE otherwise +isNotEmpty() { + [[ -z "$1" ]] && return $FALSE + return $TRUE +} + +# Returns $TRUE if $1 is an integer, $FALSE otherwise +isAnInteger() { + case $1 in + ''|*[!0-9]*) return $FALSE ;; + *) return $TRUE ;; + esac +} + +# Returns $TRUE if $1 is NOT an integer, $FALSE otherwise +isNotAnInteger() { + case $1 in + ''|*[!0-9]*) return $TRUE ;; + *) return $FALSE ;; + esac +} + +# Returns $TRUE if $1 is a floating point number (or an integer), $FALSE otherwise +isFloat() { + [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ ]] && return $TRUE + return $FALSE +} + +# Returns $TRUE if $1 is a floating point number bigger than zero, $FALSE otherwise +isFloatBiggerThanZero() { + isFloat "$1" && [[ (( $1 > 0 )) ]] && return $TRUE + return $FALSE +} + +# Returns $TRUE if $1 has a .pdf extension, false otherwsie +isPDF() { + [[ "$1" =~ ^..*\.pdf$ ]] && return $TRUE + return $FALSE +} + + +# Returns $TRUE if $1 is a file, false otherwsie +isFile() { + [[ -f "$1" ]] && return $TRUE + return $FALSE +} + + +# Returns $TRUE if $1 is NOT a file, false otherwsie +notIsFile() { + [[ -f "$1" ]] && return $FALSE + return $TRUE +} + + +# Returns $TRUE if $1 is executable, false otherwsie +isExecutable() { + [[ -x "$1" ]] && return $TRUE + return $FALSE +} + + +# Returns $TRUE if $1 is NOT executable, false otherwsie +notIsExecutable() { + [[ -x "$1" ]] && return $FALSE + return $TRUE +} + + +# Returns $TRUE if $1 is a file and executable, false otherwsie +isAvailable() { + if isFile "$1" && isExecutable "$1"; then + return $TRUE fi - - if [[ -z $PGWIDTH && -z $PGHEIGHT ]]; then - vprint " Failed" - vprint " Method: ImageMagick's Identify" - getPageSizeImagemagick + return $FALSE +} + + +# Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie +notIsAvailable() { + if notIsFile "$1" || notIsExecutable "$1"; then + return $TRUE fi - - if [[ -z $PGWIDTH && -z $PGHEIGHT ]]; then - vprint " Failed" - echo "Error when detecting PDF paper size!" - echo "All methods of detection failed" - echo "You may want to install pdfinfo or imagemagick" - exit 17 + return $FALSE +} + + +###################### PRINTING TO SCREEN ###################### + +# Prints version +printVersion() { + local vStr="" + [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution" + local strBanner="$PDFSCALE_NAME v$VERSION$vStr" + if [[ $1 -eq 2 ]]; then + printError "$strBanner" + elif [[ $1 -eq 3 ]]; then + local extra="$(isNotEmpty "$2" && echo "$2")" + echo "$strBanner$extra" + else + vprint "$strBanner" fi } +# Prints the scale factor to screen, or custom message +vPrintScaleFactor() { + local scaleMsg="$SCALE" + isNotEmpty "$1" && scaleMsg="$1" + vprint " Scale Factor: $scaleMsg" +} -# Parse options -while getopts ":vhVs:m:" o; do - case "${o}" in - v) - ((VERBOSE++)) - ;; - h) - printHelp - exit 0 - ;; - V) - printVersion - exit 0 - ;; - s) - parseScale ${OPTARG} - ;; - m) - parseMode ${OPTARG} - ;; - *) - usage - ;; - esac -done -shift $((OPTIND-1)) +# Prints help info +printHelp() { + printVersion 3 + local paperList="$(printPaperNames)" + echo " +Usage: $PDFSCALE_NAME + $PDFSCALE_NAME -i + $PDFSCALE_NAME [-v] [-s ] [-m ] [outfile.pdf] + $PDFSCALE_NAME [-v] [-r ] [-f ] [-a ] [outfile.pdf] + $PDFSCALE_NAME -p + $PDFSCALE_NAME -h + $PDFSCALE_NAME -V + +Parameters: + -v Verbose mode, prints extra information + Use twice for timestamp + -h Print this help to screen and exits + -V Prints version to screen and exits + -m Page size Detection mode + May disable the Adaptive Mode + -i Prints Page Size information to screen and exits + -s Changes the scaling factor or forces scaling + Defaults: $SCALE / no scaling (resize mode) + MUST be a number bigger than zero + Eg. -s 0.8 for 80% of the original size + -r Triggers the Resize Paper Mode + Resize PDF paper proportionally + Uses a valid paper name or a custom defined paper + -f Flip Detection Mode, defaults to 'auto'. + Inverts Width <-> Height of a Resized PDF. + Modes: a, auto - automatic detection, default + f, force - forces flip W <-> H + d, disable - disables flipping + -a GS Auto-Rotation Setting, defaults to 'PageByPage'. + Setting for GS -dAutoRotatePages. + Modes: p, pagebypage - auto-rotates pages individually + a, all - rotates all pages (or none) depending + on a kind of \"majority decision\" + n, none - retains orientation of each page + -p Prints Ghostscript paper info tables to screen + +Scaling Mode: + The default mode of operation is scaling mode with fixed paper + size and scaling pre-set to $SCALE. By not using the resize mode + you are using scaling mode. Flip-Detection and Auto-Rotation are + disabled in Scaling mode. + +Resize Paper Mode: + Disables the default scaling factor! ($SCALE) + Changes the PDF Paper Size in points. Will fit-to-page. + +Mixed Mode: + In mixed mode both the -s option and -r option must be specified. + The PDF will be first resized then scaled. + +Output filename: + The output filename is optional. If no file name is passed + the output file will have the same name/destination of the + input file with added suffixes: + .SCALED.pdf is added to scaled files + ..pdf is added to resized files + ..SCALED.pdf is added in mixed mode + +Page Size Detection Modes: + a, adaptive Default mode, tries all the methods below + g, grep Forces the use of grep method + m, mdls Forces the use of MacOS Quartz mdls + p, pdfinfo Forces the use of PDFInfo + i, identify Forces the use of ImageMagick's Identify + +Valid Paper Names: (case-insensitive) +$paperList +Custom Paper Size: + Paper size can be set manually in Milimeters, Inches or Points. + Use: $PDFSCALE_NAME -r 'custom ' + Ex: $PDFSCALE_NAME -r 'custom mm 300 300' + Measurements can be: mm, inch, pts. + Custom paper definition MUST be quoted into a single parameter. + Actual size is applied in points (mms and inches are transformed). -######### START EXECUTION +Additional Notes: + - Adaptive Page size detection will try different modes until + it gets a page size. You can force a mode with -m 'mode'. + - Options must be passed before the file names to be parsed. + - Having the extension .pdf on the output file name is optional, + it will be added if not present. + - File and folder names with spaces should be quoted or escaped. + - The scaling is centered and using a scale bigger than 1 may + result on cropping parts of the pdf. + - Most of the options are case-insensitive, Ex: -m PdFinFo -#Intro message -vprint "$(basename $0) v$VERSION - Verbose execution" +Examples: + $PDFSCALE_NAME myPdfFile.pdf + $PDFSCALE_NAME -i '/home/My Folder/My PDF File.pdf' + $PDFSCALE_NAME myPdfFile.pdf \"My Scaled Pdf\" + $PDFSCALE_NAME -v -v myPdfFile.pdf + $PDFSCALE_NAME -s 0.85 myPdfFile.pdf My\\ Scaled\\ Pdf.pdf + $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf + $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf + $PDFSCALE_NAME -r A4 myPdfFile.pdf + $PDFSCALE_NAME -v -v -r \"custom mm 252 356\" -s 0.9 -f \"../input file.pdf\" \"../my new pdf\" +" +} -# Dependencies -vprint "Checking for ghostscript and bcmath" -command -v gs >/dev/null 2>&1 || printDependency 'ghostscript' -command -v bc >/dev/null 2>&1 || printDependency 'bc' -if [[ $MODE = "IDENTIFY" ]]; then - vprint "Checking for imagemagick's identify" - command -v identify >/dev/null 2>&1 || printDependency 'imagemagick' -fi -if [[ $MODE = "PDFINFO" ]]; then - vprint "Checking for pdfinfo" - command -v pdfinfo >/dev/null 2>&1 || printDependency 'pdfinfo' -fi +# Prints usage info +usage() { + [[ "$2" != 'nobanner' ]] && printVersion 2 + isNotEmpty "$1" && printError "$1" + printError "Usage: $PDFSCALE_NAME [-v] [-s ] [-m ] [outfile.pdf]" + printError "Try: $PDFSCALE_NAME -h # for help" +} -# Get dependency binaries -GSBIN="$(which gs 2>/dev/null)" -BCBIN="$(which bc 2>/dev/null)" -IDBIN=$(which identify 2>/dev/null) -if [[ $OSNAME = "Darwin" ]]; then - MDLSBIN="$(which mdls 2>/dev/null)" -else - PDFINFOBIN="$(which pdfinfo 2>/dev/null)" -fi +# Prints Verbose information +vprint() { + [[ $VERBOSE -eq 0 ]] && return $TRUE + timestamp="" + [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | " + echo "$timestamp$1" +} -# Verbose scale info -vprint " Scale factor: $SCALE" +# Prints dependency information and aborts execution +printDependency() { + #printVersion 2 + local brewName="$1" + [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf" + printError $'\n'"ERROR! You need to install the package '$1'"$'\n' + printError "Linux apt-get.: sudo apt-get install $1" + printError "Linux yum.....: sudo yum install $1" + printError "MacOS homebrew: brew install $brewName" + printError $'\n'"Aborting..." + exit $EXIT_MISSING_DEPENDENCY +} -# Validate args -[[ $# -lt 1 ]] && { usage; exit 1; } -INFILEPDF="$1" -[[ "$INFILEPDF" =~ ^..*\.pdf$ ]] || { usage; exit 2; } -[[ -f "$INFILEPDF" ]] || { echo "Error! File not found: $INFILEPDF"; exit 3; } -vprint " Input file: $INFILEPDF" +# Prints initialization errors and aborts execution +initError() { + local errStr="$1" + local exitStat=$2 + isEmpty "$exitStat" && exitStat=$EXIT_ERROR + usage "ERROR! $errStr" "$3" + exit $exitStat +} -# Parse output filename -if [[ -z $2 ]]; then - OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf" -else - OUTFILEPDF="${2%.pdf}.pdf" -fi -vprint " Output file: $OUTFILEPDF" +# Prints to stderr +printError() { + echo >&2 "$@" +} -getPageSize -vprint " Width: $PGWIDTH postscript-points" -vprint " Height: $PGHEIGHT postscript-points" -# Compute translation factors (to center page. -XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN") -YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN") -vprint " Translation X: $XTRANS" -vprint " Translation Y: $YTRANS" +########################## EXECUTION ########################### +initDeps +getOptions "${@}" +main +exit $? -# Do it. -"$GSBIN" \ --q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \ --dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \ --dColorConversionStrategy=/LeaveColorUnchanged \ --dSubsetFonts=true -dEmbedAllFonts=true \ --dDEVICEWIDTH=$PGWIDTH -dDEVICEHEIGHT=$PGHEIGHT \ --sOutputFile="$OUTFILEPDF" \ --c "<> setpagedevice" \ --f "$INFILEPDF"