Bash Script to scale and/or resize PDFs from the command line.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2264 lines
82 KiB

  1. #!/usr/bin/env bash
  2. # pdfScale.sh
  3. #
  4. # Scale PDF to specified percentage of original size.
  5. #
  6. # Gustavo Arnosti Neves - 2016 / 07 / 10
  7. # Latest Version - 2018 / 08 / 09
  8. #
  9. # This script: https://github.com/tavinus/pdfScale
  10. #
  11. # Related: http://ma.juii.net/blog/scale-page-content-of-pdf-files
  12. # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a
  13. VERSION="2.4.2"
  14. ###################### EXTERNAL PROGRAMS #######################
  15. GSBIN="" # GhostScript Binary
  16. BCBIN="" # BC Math Binary
  17. IDBIN="" # Identify Binary
  18. PDFINFOBIN="" # PDF Info Binary
  19. MDLSBIN="" # MacOS mdls Binary
  20. ##################### ENVIRONMENT SET-UP #######################
  21. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  22. LC_ALL="C" # Some languages use , as decimal token
  23. LC_CTYPE="C"
  24. LC_NUMERIC="C"
  25. TRUE=0 # Silly stuff
  26. FALSE=1
  27. ########################### GLOBALS ############################
  28. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  29. VERBOSE=0 # verbosity Level
  30. PDFSCALE_NAME="$(basename $0)" # simplified name of this script
  31. OSNAME="$(uname 2>/dev/null)" # Check where we are running
  32. GS_RUN_STATUS="" # Holds GS error messages, signals errors
  33. INFILEPDF="" # Input PDF file name
  34. OUTFILEPDF="" # Output PDF file name
  35. JUST_IDENTIFY=$FALSE # Flag to just show PDF info
  36. ABORT_ON_OVERWRITE=$FALSE # Flag to abort if OUTFILEPDF already exists
  37. ADAPTIVEMODE=$TRUE # Automatically try to guess best mode
  38. AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, disabled in resize mode
  39. MODE="" # Which page size detection to use
  40. RESIZE_PAPER_TYPE="" # Pre-defined paper to use
  41. CUSTOM_RESIZE_PAPER=$FALSE # If we are using a custom-defined paper
  42. FLIP_DETECTION=$TRUE # If we should run the Flip-detection
  43. FLIP_FORCE=$FALSE # If we should force Flipping
  44. AUTO_ROTATION='/PageByPage' # GS call auto-rotation setting
  45. PGWIDTH="" # Input PDF Page Width
  46. PGHEIGHT="" # Input PDF Page Height
  47. RESIZE_WIDTH="" # Resized PDF Page Width
  48. RESIZE_HEIGHT="" # Resized PDF Page Height
  49. ############################# Image resolution (dpi)
  50. IMAGE_RESOLUTION=300 # 300 is /Printer default
  51. ############################# Image compression setting
  52. # default screen ebook printer prepress
  53. # ColorImageDownsampleType /Subsample /Average /Bicubic /Bicubic /Bicubic
  54. IMAGE_DOWNSAMPLE_TYPE='/Bicubic'
  55. ############################# default PDF profile
  56. # /screen /ebook /printer /prepress /default
  57. # -dPDFSETTINGS=/screen (screen-view-only quality, 72 dpi images)
  58. # -dPDFSETTINGS=/ebook (low quality, 150 dpi images)
  59. # -dPDFSETTINGS=/printer (high quality, 300 dpi images)
  60. # -dPDFSETTINGS=/prepress (high quality, color preserving, 300 dpi imgs)
  61. # -dPDFSETTINGS=/default (almost identical to /screen)
  62. PDF_SETTINGS='/printer'
  63. ############################# default Scaling alignment
  64. VERT_ALIGN="CENTER"
  65. HOR_ALIGN="CENTER"
  66. ############################# Translation Offset to apply
  67. XTRANSOFFSET=0.0
  68. YTRANSOFFSET=0.0
  69. ############################# Background/Bleed color creation
  70. BACKGROUNDTYPE="NONE" # Should be NONE, CMYK or RGB only
  71. BACKGROUNDCOLOR="" # Color parameters for CMYK(4) or RGB(3)
  72. BACKGROUNDCALL="" # Actual PS call to be embedded
  73. BACKGROUNDLOG="No background (default)"
  74. ############################# Execution Flags
  75. SIMULATE=$FALSE # Avoid execution
  76. PRINT_GS_CALL=$FALSE # Print GS Call to stdout
  77. GS_CALL_STRING="" # Buffer
  78. ############################# Project Info
  79. PROJECT_NAME="pdfScale"
  80. PROJECT_URL="https://github.com/tavinus/$PROJECT_NAME"
  81. PROJECT_BRANCH='master'
  82. HTTPS_INSECURE=$FALSE
  83. ASSUME_YES=$FALSE
  84. RUN_SELF_INSTALL=$FALSE
  85. TARGET_LOC="/usr/local/bin/pdfscale"
  86. ########################## EXIT FLAGS ##########################
  87. EXIT_SUCCESS=0
  88. EXIT_ERROR=1
  89. EXIT_INVALID_PAGE_SIZE_DETECTED=10
  90. EXIT_FILE_NOT_FOUND=20
  91. EXIT_INPUT_NOT_PDF=21
  92. EXIT_INVALID_OPTION=22
  93. EXIT_NO_INPUT_FILE=23
  94. EXIT_INVALID_SCALE=24
  95. EXIT_MISSING_DEPENDENCY=25
  96. EXIT_IMAGEMAGIK_NOT_FOUND=26
  97. EXIT_MAC_MDLS_NOT_FOUND=27
  98. EXIT_PDFINFO_NOT_FOUND=28
  99. EXIT_NOWRITE_PERMISSION=29
  100. EXIT_NOREAD_PERMISSION=30
  101. EXIT_TEMP_FILE_EXISTS=40
  102. EXIT_INVALID_PAPER_SIZE=50
  103. EXIT_INVALID_IMAGE_RESOLUTION=51
  104. ############################# MAIN #############################
  105. # Main function called at the end
  106. main() {
  107. printPDFSizes # may exit here
  108. local finalRet=$EXIT_ERROR
  109. if isMixedMode; then
  110. initMain " Mixed Tasks: Resize & Scale"
  111. local tempFile=""
  112. local tempSuffix="$RANDOM$RANDOM""_TEMP_$RANDOM$RANDOM.pdf"
  113. outputFile="$OUTFILEPDF" # backup outFile name
  114. tempFile="${OUTFILEPDF%.pdf}.$tempSuffix" # set a temp file name
  115. if isFile "$tempFile"; then
  116. printError $'Error! Temporary file name already exists!\n'"File: $tempFile"$'\nAborting execution to avoid overwriting the file.\nPlease Try again...'
  117. exit $EXIT_TEMP_FILE_EXISTS
  118. fi
  119. OUTFILEPDF="$tempFile" # set output to tmp file
  120. pageResize # resize to tmp file
  121. finalRet=$?
  122. INFILEPDF="$tempFile" # get tmp file as input
  123. OUTFILEPDF="$outputFile" # reset final target
  124. PGWIDTH=$RESIZE_WIDTH # we already know the new page size
  125. PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize)
  126. vPrintPageSizes ' New'
  127. vPrintScaleFactor
  128. pageScale # scale the resized pdf
  129. finalRet=$(($finalRet+$?))
  130. # remove tmp file
  131. if isFile "$tempFile"; then
  132. rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile"
  133. fi
  134. elif isResizeMode; then
  135. initMain " Single Task: Resize PDF Paper"
  136. vPrintScaleFactor "Disabled (resize only)"
  137. pageResize
  138. finalRet=$?
  139. else
  140. initMain " Single Task: Scale PDF Contents"
  141. local scaleMode=""
  142. isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)'
  143. vPrintScaleFactor "$SCALE $scaleMode"
  144. pageScale
  145. finalRet=$?
  146. fi
  147. if [[ $finalRet -eq $EXIT_SUCCESS ]] && isEmpty "$GS_RUN_STATUS"; then
  148. if isDryRun; then
  149. vprint " Final Status: Simulation completed successfully"
  150. else
  151. vprint " Final Status: File created successfully"
  152. fi
  153. else
  154. vprint " Final Status: Error detected. Exit status: $finalRet"
  155. printError "PdfScale: ERROR!"$'\n'"Ghostscript Debug Info:"$'\n'"$GS_RUN_STATUS"
  156. fi
  157. if isNotEmpty "$GS_CALL_STRING" && shouldPrintGSCall; then
  158. printf "%s" "$GS_CALL_STRING"
  159. fi
  160. return $finalRet
  161. }
  162. # Initializes PDF processing for all modes of operation
  163. initMain() {
  164. printVersion 1 'verbose'
  165. isNotEmpty "$1" && vprint "$1"
  166. local sim="FALSE"
  167. isDryRun && sim="TRUE (Simulating)"
  168. vprint " Dry-Run: $sim"
  169. vPrintFileInfo
  170. getPageSize
  171. vPrintPageSizes ' Source'
  172. }
  173. # Prints PDF Info and exits with $EXIT_SUCCESS, but only if $JUST_IDENTIFY is $TRUE
  174. printPDFSizes() {
  175. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  176. VERBOSE=0
  177. printVersion 3 " - Paper Sizes"
  178. getPageSize || initError "Could not get pagesize!"
  179. local paperType="$(getGSPaperName $PGWIDTH $PGHEIGHT)"
  180. isEmpty "$paperType" && paperType="Custom Paper Size"
  181. printf '%s\n' "------------+-----------------------------"
  182. printf " File | %s\n" "$(basename "$INFILEPDF")"
  183. printf " Paper Type | %s\n" "$paperType"
  184. printf '%s\n' "------------+-----------------------------"
  185. printf '%s\n' " | WIDTH x HEIGHT"
  186. printf " Points | %+8s x %-8s\n" "$PGWIDTH" "$PGHEIGHT"
  187. printf " Milimeters | %+8s x %-8s\n" "$(pointsToMilimeters $PGWIDTH)" "$(pointsToMilimeters $PGHEIGHT)"
  188. printf " Inches | %+8s x %-8s\n" "$(pointsToInches $PGWIDTH)" "$(pointsToInches $PGHEIGHT)"
  189. exit $EXIT_SUCCESS
  190. fi
  191. return $EXIT_SUCCESS
  192. }
  193. ###################### GHOSTSCRIPT CALLS #######################
  194. # Runs the ghostscript scaling script
  195. pageScale() {
  196. # Compute translation factors to position pages
  197. CENTERXTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  198. CENTERYTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  199. BXTRANS=$CENTERXTRANS
  200. BYTRANS=$CENTERYTRANS
  201. if [[ "$VERT_ALIGN" = "TOP" ]]; then
  202. BYTRANS=$(echo "scale=6; 2*$CENTERYTRANS" | "$BCBIN")
  203. elif [[ "$VERT_ALIGN" = "BOTTOM" ]]; then
  204. BYTRANS=0
  205. fi
  206. if [[ "$HOR_ALIGN" = "LEFT" ]]; then
  207. BXTRANS=0
  208. elif [[ "$HOR_ALIGN" = "RIGHT" ]]; then
  209. BXTRANS=$(echo "scale=6; 2*$CENTERXTRANS" | "$BCBIN")
  210. fi
  211. vprint " Vert-Align: $VERT_ALIGN"
  212. vprint " Hor-Align: $HOR_ALIGN"
  213. XTRANS=$(echo "scale=6; $BXTRANS + $XTRANSOFFSET" | "$BCBIN")
  214. YTRANS=$(echo "scale=6; $BYTRANS + $YTRANSOFFSET" | "$BCBIN")
  215. vprint "$(printf ' Translation X: %.2f = %.2f + %.2f (offset)' $XTRANS $BXTRANS $XTRANSOFFSET)"
  216. vprint "$(printf ' Translation Y: %.2f = %.2f + %.2f (offset)' $YTRANS $BYTRANS $YTRANSOFFSET)"
  217. local increase=$(echo "scale=0; (($SCALE - 1) * 100)/1" | "$BCBIN")
  218. vprint " Run Scaling: $increase %"
  219. vprint " Background: $BACKGROUNDLOG"
  220. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageScale 2>&1)"
  221. GS_CALL_STRING="$GS_CALL_STRING"$'[GS SCALE CALL STARTS]\n'"$(gsPrintPageScale)"$'\n[GS SCALE CALL ENDS]\n'
  222. return $? # Last command is always returned I think
  223. }
  224. # Runs GS call for scaling, nothing else should run here
  225. gsPageScale() {
  226. if isDryRun; then
  227. return $TRUE
  228. fi
  229. # Scale page
  230. "$GSBIN" \
  231. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  232. -dCompatibilityLevel="1.5" -dPDFSETTINGS="$PDF_SETTINGS" \
  233. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  234. -dColorImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" -dGrayImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" \
  235. -dColorConversionStrategy=/LeaveColorUnchanged \
  236. -dSubsetFonts=true -dEmbedAllFonts=true \
  237. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  238. -sOutputFile="$OUTFILEPDF" \
  239. -c "<</BeginPage{$BACKGROUNDCALL$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  240. -f "$INFILEPDF"
  241. }
  242. # Prints GS call for scaling
  243. gsPrintPageScale() {
  244. local _call_str=""
  245. # Print Scale page command
  246. read -d '' _call_str<< _EOF_
  247. "$GSBIN" \
  248. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  249. -dCompatibilityLevel="1.5" -dPDFSETTINGS="$PDF_SETTINGS" \
  250. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  251. -dColorImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" -dGrayImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" \
  252. -dColorConversionStrategy=/LeaveColorUnchanged \
  253. -dSubsetFonts=true -dEmbedAllFonts=true \
  254. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  255. -sOutputFile="$OUTFILEPDF" \
  256. -c "<</BeginPage{$BACKGROUNDCALL$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  257. -f "$INFILEPDF"
  258. _EOF_
  259. echo -ne "$_call_str"
  260. }
  261. # Runs the ghostscript paper resize script
  262. pageResize() {
  263. # Get paper sizes from source if not resizing
  264. isResizePaperSource && { RESIZE_WIDTH=$PGWIDTH; RESIZE_HEIGHT=$PGHEIGHT; }
  265. # Get new paper sizes if not custom or source paper
  266. isNotCustomPaper && ! isResizePaperSource && getGSPaperSize "$RESIZE_PAPER_TYPE"
  267. vprint " Auto Rotate: $(basename $AUTO_ROTATION)"
  268. runFlipDetect
  269. vprint " Run Resizing: $(uppercase "$RESIZE_PAPER_TYPE") ( "$RESIZE_WIDTH" x "$RESIZE_HEIGHT" ) pts"
  270. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageResize 2>&1)"
  271. GS_CALL_STRING="$GS_CALL_STRING"$'[GS RESIZE CALL STARTS]\n'"$(gsPrintPageResize)"$'\n[GS RESIZE CALL ENDS]\n'
  272. return $?
  273. }
  274. # Runs GS call for resizing, nothing else should run here
  275. gsPageResize() {
  276. if isDryRun; then
  277. return $TRUE
  278. fi
  279. # Change page size
  280. "$GSBIN" \
  281. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  282. -dCompatibilityLevel="1.5" -dPDFSETTINGS="$PDF_SETTINGS" \
  283. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  284. -dColorImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" -dGrayImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" \
  285. -dColorConversionStrategy=/LeaveColorUnchanged \
  286. -dSubsetFonts=true -dEmbedAllFonts=true \
  287. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  288. -dAutoRotatePages=$AUTO_ROTATION \
  289. -dFIXEDMEDIA -dPDFFitPage \
  290. -sOutputFile="$OUTFILEPDF" \
  291. -f "$INFILEPDF"
  292. return $?
  293. }
  294. # Prints GS call for resizing
  295. gsPrintPageResize() {
  296. # Print Resize page command
  297. local _call_str=""
  298. # Print Scale page command
  299. read -d '' _call_str<< _EOF_
  300. "$GSBIN" \
  301. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  302. -dCompatibilityLevel="1.5" -dPDFSETTINGS="$PDF_SETTINGS" \
  303. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  304. -dColorImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" -dGrayImageDownsampleType="$IMAGE_DOWNSAMPLE_TYPE" \
  305. -dColorConversionStrategy=/LeaveColorUnchanged \
  306. -dSubsetFonts=true -dEmbedAllFonts=true \
  307. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  308. -dAutoRotatePages=$AUTO_ROTATION \
  309. -dFIXEDMEDIA -dPDFFitPage \
  310. -sOutputFile="$OUTFILEPDF" \
  311. -f "$INFILEPDF"
  312. _EOF_
  313. echo -ne "$_call_str"
  314. }
  315. # Returns $TRUE if we should use the source paper size, $FALSE otherwise
  316. isResizePaperSource() {
  317. [[ "$RESIZE_PAPER_TYPE" = 'source' ]] && return $TRUE
  318. return $FALSE
  319. }
  320. # Filp-Detect Logic
  321. runFlipDetect() {
  322. if isFlipForced; then
  323. vprint " Flip Detect: Forced Mode!"
  324. applyFlipRevert
  325. elif isFlipDetectionEnabled && shouldFlip; then
  326. vprint " Flip Detect: Wrong orientation detected!"
  327. applyFlipRevert
  328. elif ! isFlipDetectionEnabled; then
  329. vprint " Flip Detect: Disabled"
  330. else
  331. vprint " Flip Detect: No change needed"
  332. fi
  333. }
  334. # Inverts $RESIZE_HEIGHT with $RESIZE_WIDTH
  335. applyFlipRevert() {
  336. local tmpInverter=""
  337. tmpInverter=$RESIZE_HEIGHT
  338. RESIZE_HEIGHT=$RESIZE_WIDTH
  339. RESIZE_WIDTH=$tmpInverter
  340. vprint " Inverting Width <-> Height"
  341. }
  342. # Returns the $FLIP_DETECTION flag
  343. isFlipDetectionEnabled() {
  344. return $FLIP_DETECTION
  345. }
  346. # Returns the $FLIP_FORCE flag
  347. isFlipForced() {
  348. return $FLIP_FORCE
  349. }
  350. # Returns $TRUE if the the paper size will invert orientation from source, $FALSE otherwise
  351. shouldFlip() {
  352. [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]] || [[ $PGWIDTH -lt $PGHEIGHT && $RESIZE_WIDTH -gt $RESIZE_HEIGHT ]] && return $TRUE
  353. return $FALSE
  354. }
  355. ########################## INITIALIZERS #########################
  356. # Loads external dependencies and checks for errors
  357. initDeps() {
  358. GREPBIN="$(which grep 2>/dev/null)"
  359. GSBIN="$(which gs 2>/dev/null)"
  360. BCBIN="$(which bc 2>/dev/null)"
  361. IDBIN=$(which identify 2>/dev/null)
  362. MDLSBIN="$(which mdls 2>/dev/null)"
  363. PDFINFOBIN="$(which pdfinfo 2>/dev/null)"
  364. vprint "Checking for basename, grep, ghostscript and bcmath"
  365. basename "" >/dev/null 2>&1 || printDependency 'basename'
  366. isNotAvailable "$GREPBIN" && printDependency 'grep'
  367. isNotAvailable "$GSBIN" && printDependency 'ghostscript'
  368. isNotAvailable "$BCBIN" && printDependency 'bc'
  369. return $TRUE
  370. }
  371. # Checks for dependencies errors, run after getting options
  372. checkDeps() {
  373. if [[ $MODE = "IDENTIFY" ]]; then
  374. vprint "Checking for imagemagick's identify"
  375. if isNotAvailable "$IDBIN"; then printDependency 'imagemagick'; fi
  376. fi
  377. if [[ $MODE = "PDFINFO" ]]; then
  378. vprint "Checking for pdfinfo"
  379. if isNotAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi
  380. fi
  381. if [[ $MODE = "MDLS" ]]; then
  382. vprint "Checking for MacOS mdls"
  383. if isNotAvailable "$MDLSBIN"; then
  384. initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND
  385. fi
  386. fi
  387. return $TRUE
  388. }
  389. ######################### CLI OPTIONS ##########################
  390. # Parse options
  391. getOptions() {
  392. local _optArgs=() # things that do not start with a '-'
  393. local _tgtFile="" # to set $OUTFILEPDF
  394. local _currParam="" # to enable case-insensitiveness
  395. while [ ${#} -gt 0 ]; do
  396. if [[ "${1:0:2}" = '--' ]]; then
  397. # Long Option, get lowercase version
  398. _currParam="$(lowercase ${1})"
  399. elif [[ "${1:0:1}" = '-' ]]; then
  400. # short Option, just assign
  401. _currParam="${1}"
  402. else
  403. # file name arguments, store as is and reset loop
  404. _optArgs+=("$1")
  405. shift
  406. continue
  407. fi
  408. case "$_currParam" in
  409. -v|--verbose)
  410. ((VERBOSE++))
  411. shift
  412. ;;
  413. -n|--no-overwrite|--nooverwrite)
  414. ABORT_ON_OVERWRITE=$TRUE
  415. shift
  416. ;;
  417. -h|--help)
  418. printHelp
  419. exit $EXIT_SUCCESS
  420. ;;
  421. -V|--version)
  422. printVersion 3
  423. exit $EXIT_SUCCESS
  424. ;;
  425. -i|--identify|--info)
  426. JUST_IDENTIFY=$TRUE
  427. shift
  428. ;;
  429. -s|--scale|--setscale|--set-scale)
  430. shift
  431. parseScale "$1"
  432. shift
  433. ;;
  434. -m|--mode|--paperdetect|--paper-detect|--pagesizemode|--page-size-mode)
  435. shift
  436. parseMode "$1"
  437. shift
  438. ;;
  439. -r|--resize)
  440. shift
  441. parsePaperResize "$1"
  442. shift
  443. ;;
  444. -p|--printpapers|--print-papers|--listpapers|--list-papers)
  445. printPaperInfo
  446. exit $EXIT_SUCCESS
  447. ;;
  448. -f|--flipdetection|--flip-detection|--flip-mode|--flipmode|--flipdetect|--flip-detect)
  449. shift
  450. parseFlipDetectionMode "$1"
  451. shift
  452. ;;
  453. -a|--autorotation|--auto-rotation|--autorotate|--auto-rotate)
  454. shift
  455. parseAutoRotationMode "$1"
  456. shift
  457. ;;
  458. --background-gray)
  459. shift
  460. parseGrayBackground $1
  461. shift
  462. ;;
  463. --background-rgb)
  464. shift
  465. parseRGBBackground $1
  466. shift
  467. ;;
  468. --background-cmyk)
  469. shift
  470. parseCMYKBackground $1
  471. shift
  472. ;;
  473. --pdf-settings)
  474. shift
  475. parsePDFSettings "$1"
  476. shift
  477. ;;
  478. --image-downsample)
  479. shift
  480. parseImageDownSample "$1"
  481. shift
  482. ;;
  483. --image-resolution)
  484. shift
  485. parseImageResolution "$1"
  486. shift
  487. ;;
  488. --horizontal-alignment|--hor-align|--xalign|--x-align)
  489. shift
  490. parseHorizontalAlignment "$1"
  491. shift
  492. ;;
  493. --vertical-alignment|--ver-align|--vert-align|--yalign|--y-align)
  494. shift
  495. parseVerticalAlignment "$1"
  496. shift
  497. ;;
  498. --xtrans|--xtrans-offset|--xoffset)
  499. shift
  500. parseXTransOffset "$1"
  501. shift
  502. ;;
  503. --ytrans|--ytrans-offset|--yoffset)
  504. shift
  505. parseYTransOffset "$1"
  506. shift
  507. ;;
  508. --simulate|--dry-run)
  509. SIMULATE=$TRUE
  510. shift
  511. ;;
  512. --install|--self-install)
  513. RUN_SELF_INSTALL=$TRUE
  514. shift
  515. if [[ ${1:0:1} != "-" ]]; then
  516. TARGET_LOC="$1"
  517. shift
  518. fi
  519. ;;
  520. --upgrade|--self-upgrade)
  521. RUN_SELF_UPGRADE=$TRUE
  522. shift
  523. ;;
  524. --insecure|--no-check-certificate)
  525. HTTPS_INSECURE=$TRUE
  526. shift
  527. ;;
  528. --yes|--assume-yes)
  529. ASSUME_YES=$TRUE
  530. shift
  531. ;;
  532. --print-gs-call|--gs-call)
  533. PRINT_GS_CALL=$TRUE
  534. shift
  535. ;;
  536. *)
  537. initError "Invalid Parameter: \"$1\"" $EXIT_INVALID_OPTION
  538. ;;
  539. esac
  540. done
  541. shouldInstall && selfInstall "$TARGET_LOC" # WILL EXIT HERE
  542. shouldUpgrade && selfUpgrade # WILL EXIT HERE
  543. isEmpty "${_optArgs[2]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[2]}" $EXIT_INVALID_OPTION
  544. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  545. isEmpty "${_optArgs[1]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[1]}" $EXIT_INVALID_OPTION
  546. VERBOSE=0 # remove verboseness if present
  547. fi
  548. # Validate input PDF file
  549. INFILEPDF="${_optArgs[0]}"
  550. isEmpty "$INFILEPDF" && initError "Input file is empty!" $EXIT_NO_INPUT_FILE
  551. isPDF "$INFILEPDF" || initError "Input file is not a PDF file: $INFILEPDF" $EXIT_INPUT_NOT_PDF
  552. isFile "$INFILEPDF" || initError "Input file not found: $INFILEPDF" $EXIT_FILE_NOT_FOUND
  553. isReadable "$INFILEPDF" || initError "No read access to input file: $INFILEPDF"$'\nPermission Denied' $EXIT_NOREAD_PERMISSION
  554. checkDeps
  555. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  556. return $TRUE # no need to get output file, so return already
  557. fi
  558. _tgtFile="${_optArgs[1]}"
  559. local _autoName="${INFILEPDF%.*}" # remove possible stupid extension, like .pDF
  560. if isMixedMode; then
  561. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf"
  562. elif isResizeMode; then
  563. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).pdf"
  564. else
  565. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.SCALED.pdf"
  566. fi
  567. isNotEmpty "$_tgtFile" && OUTFILEPDF="${_tgtFile%.pdf}.pdf"
  568. validateOutFile
  569. }
  570. # Returns $TRUE if the install flag is set
  571. shouldInstall() {
  572. return $RUN_SELF_INSTALL
  573. }
  574. # Returns $TRUE if the upgrade flag is set
  575. shouldUpgrade() {
  576. return $RUN_SELF_UPGRADE
  577. }
  578. # Install pdfScale
  579. selfInstall() {
  580. CURRENT_LOC="$(readlink -f $0)"
  581. TARGET_LOC="$1"
  582. isEmpty "$TARGET_LOC" && TARGET_LOC="/usr/local/bin/pdfscale"
  583. VERBOSE=0
  584. NEED_SUDO=$FALSE
  585. printVersion 3 " - Self Install"
  586. echo ""
  587. echo "Current location : $CURRENT_LOC"
  588. echo "Target location : $TARGET_LOC"
  589. if [[ "$CURRENT_LOC" = "$TARGET_LOC" ]]; then
  590. echo $'\n'"Error! Source and Target locations are the same!"
  591. echo "Cannot copy to itself..."
  592. exit $EXIT_INVALID_OPTION
  593. fi
  594. TARGET_FOLDER="$(dirname $TARGET_LOC)"
  595. local _answer="NO"
  596. if isNotDir "$TARGET_FOLDER"; then
  597. echo $'\nThe target folder does not exist\n > '"$TARGET_FOLDER"
  598. if assumeYes; then
  599. echo ''
  600. _answer="y"
  601. else
  602. read -p $'\nCreate the target folder? Y/y to continue > ' _answer
  603. _answer="$(lowercase $_answer)"
  604. fi
  605. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  606. _answer="no"
  607. if mkdir -p "$TARGET_FOLDER" 2>/dev/null; then
  608. echo " > Folder Created!"
  609. else
  610. echo $'\n'"There was an error when trying to create the folder."
  611. if assumeYes; then
  612. echo $'\nTrying again with sudo, enter password if needed > '
  613. _answer="y"
  614. else
  615. read -p $'\nDo you want to try again with sudo (as root)? Y/y to continue > ' _answer
  616. _answer="$(lowercase $_answer)"
  617. fi
  618. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  619. NEED_SUDO=$TRUE
  620. if sudo mkdir -p "$TARGET_FOLDER" 2>/dev/null; then
  621. echo "Folder Created!"
  622. else
  623. echo "There was an error when trying to create the folder."
  624. exit $EXIT_ERROR
  625. fi
  626. else
  627. echo "Exiting..."
  628. exit $EXIT_ERROR
  629. fi
  630. fi
  631. else
  632. echo "Exiting... (cancelled by user)"
  633. exit $EXIT_ERROR
  634. fi
  635. fi
  636. _answer="no"
  637. if isFile "$TARGET_LOC"; then
  638. echo $'\n'"The target file already exists: $TARGET_LOC"
  639. if assumeYes; then
  640. _answer="y"
  641. else
  642. read -p "Y/y to overwrite, anything else to cancel > " _answer
  643. _answer="$(lowercase $_answer)"
  644. fi
  645. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  646. echo "Target will be replaced!"
  647. else
  648. echo "Exiting... (cancelled by user)"
  649. exit $EXIT_ERROR
  650. fi
  651. fi
  652. if [[ $NEED_SUDO -eq $TRUE ]]; then
  653. if sudo cp "$CURRENT_LOC" "$TARGET_LOC"; then
  654. echo $'\nSuccess! Program installed!'
  655. echo " > $TARGET_LOC"
  656. exit $EXIT_SUCCESS
  657. else
  658. echo "There was an error when trying to install the program."
  659. exit $EXIT_ERROR
  660. fi
  661. fi
  662. if cp "$CURRENT_LOC" "$TARGET_LOC"; then
  663. echo $'\nSuccess! Program installed!'
  664. echo " > $TARGET_LOC"
  665. exit $EXIT_SUCCESS
  666. else
  667. _answer="no"
  668. echo "There was an error when trying to install pdfScale."
  669. if assumeYes; then
  670. echo $'\nTrying again with sudo, enter password if needed > '
  671. _answer="y"
  672. else
  673. read -p $'Do you want to try again with sudo (as root)? Y/y to continue > ' _answer
  674. _answer="$(lowercase $_answer)"
  675. fi
  676. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  677. NEED_SUDO=$TRUE
  678. if sudo cp "$CURRENT_LOC" "$TARGET_LOC"; then
  679. echo $'\nSuccess! Program installed!'
  680. echo " > $TARGET_LOC"
  681. exit $EXIT_SUCCESS
  682. else
  683. echo "There was an error when trying to install the program."
  684. exit $EXIT_ERROR
  685. fi
  686. else
  687. echo "Exiting... (cancelled by user)"
  688. exit $EXIT_ERROR
  689. fi
  690. fi
  691. exit $EXIT_ERROR
  692. }
  693. # Tries to download with curl or wget
  694. getUrl() {
  695. useInsecure && echo $'\nHTTPS Insecure flag is enabled!\nCertificates will be ignored by curl/wget\n'
  696. local url="$1"
  697. local target="$2"
  698. local _stat=""
  699. if isEmpty "$url" || isEmpty "$target"; then
  700. echo "Error! Invalid parameters for download."
  701. echo "URL > $url"
  702. echo "TARGET > $target"
  703. exit $EXIT_INVALID_OPTION
  704. fi
  705. WGET_BIN="$(which wget 2>/dev/null)"
  706. CURL_BIN="$(which curl 2>/dev/null)"
  707. if isExecutable "$WGET_BIN"; then
  708. useInsecure && WGET_BIN="$WGET_BIN --no-check-certificate"
  709. echo "Downloading file with wget"
  710. _stat="$($WGET_BIN -O "$target" "$url" 2>&1)"
  711. if [[ $? -eq 0 ]]; then
  712. return $TRUE
  713. else
  714. echo "Error when downloading file!"
  715. echo " > $url"
  716. echo "Status:"
  717. echo "$_stat"
  718. exit $EXIT_ERROR
  719. fi
  720. elif isExecutable "$CURL_BIN"; then
  721. useInsecure && CURL_BIN="$CURL_BIN --insecure"
  722. echo "Downloading file with curl"
  723. _stat="$($CURL_BIN -o "$target" "$url" 2>&1)"
  724. if [[ $? -eq 0 ]]; then
  725. return $TRUE
  726. else
  727. echo "Error when downloading file!"
  728. echo " > $url"
  729. echo "Status:"
  730. echo "$_stat"
  731. exit $EXIT_ERROR
  732. fi
  733. else
  734. echo "Error! Could not find Wget or Curl to perform download."
  735. echo "Please install either curl or wget and try again."
  736. exit $EXIT_FILE_NOT_FOUND
  737. fi
  738. }
  739. # Tries to remove temporary files from upgrade
  740. clearUpgrade() {
  741. echo $'\nCleaning up downloaded files from /tmp'
  742. if isFile "$TMP_TARGET"; then
  743. echo -n " > $TMP_TARGET > "
  744. rm "$TMP_TARGET" 2>/dev/null && echo "Ok" || echo "Fail"
  745. else
  746. echo " > no temporary tarball was found to remove"
  747. fi
  748. if isDir "$TMP_EXTRACTED"; then
  749. echo -n " > $TMP_EXTRACTED > "
  750. rm -rf "$TMP_EXTRACTED" 2>/dev/null && echo "Ok" || echo "Fail"
  751. else
  752. echo " > no temporary master folder was found to remove"
  753. fi
  754. }
  755. # Exit upgrade with message and status code
  756. # $1 Mensagem (printed if not empty)
  757. # $2 Status (defaults to $EXIT_ERROR)
  758. exitUpgrade() {
  759. isDir "$_cwd" && cd "$_cwd"
  760. isNotEmpty "$1" && echo "$1"
  761. clearUpgrade
  762. isNotEmpty "$2" && exit $2
  763. exit $EXIT_ERROR
  764. }
  765. # Downloads current version from github's MASTER branch
  766. selfUpgrade() {
  767. CURRENT_LOC="$(readlink -f $0)"
  768. _cwd="$(pwd)"
  769. local _cur_tstamp="$(date '+%Y%m%d-%H%M%S')"
  770. TMP_DIR='/tmp'
  771. TMP_TARGET="$TMP_DIR/pdfScale_$_cur_tstamp.tar.gz"
  772. TMP_EXTRACTED="$TMP_DIR/$PROJECT_NAME-$PROJECT_BRANCH"
  773. local _answer="no"
  774. printVersion 3 " - Self Upgrade"
  775. echo $'\n'"Preparing download to temp folder"
  776. echo " > $TMP_TARGET"
  777. getUrl "$PROJECT_URL/archive/$PROJECT_BRANCH.tar.gz" "$TMP_TARGET"
  778. if isNotFile "$TMP_TARGET"; then
  779. echo "Error! Could not find downloaded file!"
  780. exit $EXIT_FILE_NOT_FOUND
  781. fi
  782. echo $'\n'"Extracting compressed file"
  783. cd "$TMP_DIR"
  784. if ! (tar xzf "$TMP_TARGET" 2>/dev/null || gtar xzf "$TMP_TARGET" 2>/dev/null); then
  785. exitUpgrade "Extraction error."
  786. fi
  787. if ! cd "$TMP_EXTRACTED" 2>/dev/null; then
  788. exitUpgrade $'Error when accessing temporary folder\n > '"$TMP_EXTRACTED"
  789. fi
  790. if ! chmod +x pdfScale.sh; then
  791. exitUpgrade $'Error when setting new pdfScale to executable\n > '"$TMP_EXTRACTED/pdfScale.sh"
  792. fi
  793. local newver="$(./pdfScale.sh --version 2>/dev/null)"
  794. local curver="$(printVersion 3 2>/dev/null)"
  795. newver=($newver)
  796. curver=($curver)
  797. newver=${newver[1]#v}
  798. curver=${curver[1]#v}
  799. echo $'\n'" Current Version is: $curver"
  800. echo "Downloaded Version is: $newver"$'\n'
  801. if [[ "$newver" = "$curver" ]]; then
  802. echo "Seems like we have downloaded the same version that is installed."
  803. elif isBiggerVersion "$newver" "$curver"; then
  804. echo "Seems like the downloaded version is newer that the one installed."
  805. elif isBiggerVersion "$curver" "$newver"; then
  806. echo "Seems like the downloaded version is older that the one installed."
  807. echo "It is basically a miracle or you have came from the future with this version!"
  808. echo "BE CAREFUL NOT TO DELETE THE BETA/ALPHA VERSION WITH THIS UPDATE!"
  809. else
  810. exitUpgrade "An unidentified error has ocurred. Exiting..."
  811. fi
  812. if assumeYes; then
  813. echo $'\n'"Assume yes activated, current version will be replaced with master branch"
  814. _answer="y"
  815. else
  816. echo $'\n'"Are you sure that you want to replace the current installation with the downloaded one?"
  817. read -p "Y/y to continue, anything else to cancel > " _answer
  818. _answer="$(lowercase $_answer)"
  819. fi
  820. echo
  821. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  822. echo "Upgrading..."
  823. if cp "./pdfScale.sh" "$CURRENT_LOC" 2>/dev/null; then
  824. exitUpgrade $'\n'"Success! Upgrade finished!"$'\n'" > $CURRENT_LOC" $EXIT_SUCCESS
  825. else
  826. _answer="no"
  827. echo $'\n'"There was an error when copying the new version."
  828. if assumeYes; then
  829. echo $'\nAssume yes activated, retrying with sudo.\nEnter password if needed > \n'
  830. _answer="y"
  831. else
  832. echo "Do you want to retry using sudo (as root)?"
  833. read -p "Y/y to continue, anything else to cancel > " _answer
  834. fi
  835. _answer="$(lowercase $_answer)"
  836. if [[ "$_answer" = "y" || "$_answer" = "yes" ]]; then
  837. echo "Upgrading with sudo..."
  838. if sudo cp "./pdfScale.sh" "$CURRENT_LOC" 2>/dev/null; then
  839. exitUpgrade $'\n'"Success! Upgrade finished!"$'\n'" > $CURRENT_LOC" $EXIT_SUCCESS
  840. else
  841. exitUpgrade "There was an error when copying the new version."
  842. fi
  843. else
  844. exitUpgrade "Exiting... (cancelled by user)"
  845. fi
  846. fi
  847. exitUpgrade "An unidentified error has ocurred. Exiting..."
  848. else
  849. exitUpgrade "Exiting... (cancelled by user)"
  850. fi
  851. exitUpgrade "An unidentified error has ocurred. Exiting..."
  852. }
  853. # Compares versions with x.x.x format
  854. isBiggerVersion() {
  855. local OIFS=$IFS
  856. IFS='.'
  857. local _first=($1)
  858. local _second=($2)
  859. local _ret=$FALSE
  860. if [[ ${_first[0]} -gt ${_second[0]} ]]; then
  861. _ret=$TRUE
  862. elif [[ ${_first[0]} -lt ${_second[0]} ]]; then
  863. _ret=$FALSE
  864. elif [[ ${_first[1]} -gt ${_second[1]} ]]; then
  865. _ret=$TRUE
  866. elif [[ ${_first[1]} -lt ${_second[1]} ]]; then
  867. _ret=$FALSE
  868. elif [[ ${_first[2]} -gt ${_second[2]} ]]; then
  869. _ret=$TRUE
  870. elif [[ ${_first[2]} -lt ${_second[2]} ]]; then
  871. _ret=$FALSE
  872. fi
  873. IFS=$OIFS
  874. return $_ret
  875. }
  876. # Checks if output file is valid and writable
  877. validateOutFile() {
  878. local _tgtDir="$(dirname "$OUTFILEPDF")"
  879. isDir "$_tgtDir" || initError "Output directory does not exist!"$'\n'"Target Dir: $_tgtDir" $EXIT_NOWRITE_PERMISSION
  880. isAbortOnOverwrite && isFile "$OUTFILEPDF" && initError $'Output file already exists and --no-overwrite was used!\nRemove the "-n" or "--no-overwrite" option if you want to overwrite the file\n'"Target File: $OUTFILEPDF" $EXIT_NOWRITE_PERMISSION
  881. isTouchable "$OUTFILEPDF" || initError "Could not get write permission for output file!"$'\n'"Target File: $OUTFILEPDF"$'\nPermission Denied' $EXIT_NOWRITE_PERMISSION
  882. }
  883. # Returns $TRUE if we should not overwrite $OUTFILEPDF, $FALSE otherwise
  884. isAbortOnOverwrite() {
  885. return $ABORT_ON_OVERWRITE
  886. }
  887. # Returns $TRUE if we should print the GS call to stdout
  888. shouldPrintGSCall() {
  889. return $PRINT_GS_CALL
  890. }
  891. # Returns $TRUE if we are simulating, dry-run (no GS execution)
  892. isDryRun() {
  893. return $SIMULATE
  894. }
  895. # Parses and validates the scaling factor
  896. parseScale() {
  897. AUTOMATIC_SCALING=$FALSE
  898. if ! isFloatBiggerThanZero "$1"; then
  899. printError "Invalid factor: $1"
  900. printError "The factor must be a floating point number greater than 0"
  901. printError "Example: for 80% use 0.8"
  902. exit $EXIT_INVALID_SCALE
  903. fi
  904. SCALE="$1"
  905. }
  906. # Parse a forced mode of operation
  907. parseMode() {
  908. local param="$(lowercase $1)"
  909. case "${param}" in
  910. c|catgrep|'cat+grep'|grep|g)
  911. ADAPTIVEMODE=$FALSE
  912. MODE="CATGREP"
  913. return $TRUE
  914. ;;
  915. i|imagemagick|identify)
  916. ADAPTIVEMODE=$FALSE
  917. MODE="IDENTIFY"
  918. return $TRUE
  919. ;;
  920. m|mdls|quartz|mac)
  921. ADAPTIVEMODE=$FALSE
  922. MODE="MDLS"
  923. return $TRUE
  924. ;;
  925. p|pdfinfo)
  926. ADAPTIVEMODE=$FALSE
  927. MODE="PDFINFO"
  928. return $TRUE
  929. ;;
  930. a|auto|automatic|adaptive)
  931. ADAPTIVEMODE=$TRUE
  932. MODE=""
  933. return $TRUE
  934. ;;
  935. *)
  936. initError "Invalid PDF Size Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  937. return $FALSE
  938. ;;
  939. esac
  940. return $FALSE
  941. }
  942. # Parses and validates the scaling factor
  943. parseFlipDetectionMode() {
  944. local param="$(lowercase $1)"
  945. case "${param}" in
  946. d|disable)
  947. FLIP_DETECTION=$FALSE
  948. FLIP_FORCE=$FALSE
  949. ;;
  950. f|force)
  951. FLIP_DETECTION=$FALSE
  952. FLIP_FORCE=$TRUE
  953. ;;
  954. a|auto|automatic)
  955. FLIP_DETECTION=$TRUE
  956. FLIP_FORCE=$FALSE
  957. ;;
  958. *)
  959. initError "Invalid Flip Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  960. return $FALSE
  961. ;;
  962. esac
  963. }
  964. # Parses and validates the scaling factor
  965. parseAutoRotationMode() {
  966. local param="$(lowercase $1)"
  967. case "${param}" in
  968. n|none|'/none')
  969. AUTO_ROTATION='/None'
  970. ;;
  971. a|all|'/all')
  972. AUTO_ROTATION='/All'
  973. ;;
  974. p|pagebypage|'/pagebypage'|auto)
  975. AUTO_ROTATION='/PageByPage'
  976. ;;
  977. *)
  978. initError "Invalid Auto Rotation Mode: \"$1\"" $EXIT_INVALID_OPTION
  979. return $FALSE
  980. ;;
  981. esac
  982. }
  983. # Validades the a paper resize CLI option and sets the paper to $RESIZE_PAPER_TYPE
  984. parsePaperResize() {
  985. isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE
  986. local lowercasePaper="$(lowercase $1)"
  987. local customPaper=($lowercasePaper)
  988. if [[ "$customPaper" = 'same' || "$customPaper" = 'keep' || "$customPaper" = 'source' ]]; then
  989. RESIZE_PAPER_TYPE='source'
  990. elif [[ "${customPaper[0]}" = 'custom' ]]; then
  991. if isNotValidMeasure "${customPaper[1]}" || ! isFloatBiggerThanZero "${customPaper[2]}" || ! isFloatBiggerThanZero "${customPaper[3]}"; then
  992. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  993. fi
  994. RESIZE_PAPER_TYPE="custom"
  995. CUSTOM_RESIZE_PAPER=$TRUE
  996. if isMilimeter "${customPaper[1]}"; then
  997. RESIZE_WIDTH="$(milimetersToPoints "${customPaper[2]}")"
  998. RESIZE_HEIGHT="$(milimetersToPoints "${customPaper[3]}")"
  999. elif isInch "${customPaper[1]}"; then
  1000. RESIZE_WIDTH="$(inchesToPoints "${customPaper[2]}")"
  1001. RESIZE_HEIGHT="$(inchesToPoints "${customPaper[3]}")"
  1002. elif isPoint "${customPaper[1]}"; then
  1003. RESIZE_WIDTH="${customPaper[2]}"
  1004. RESIZE_HEIGHT="${customPaper[3]}"
  1005. else
  1006. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  1007. fi
  1008. else
  1009. isPaperName "$lowercasePaper" || initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE
  1010. RESIZE_PAPER_TYPE="$lowercasePaper"
  1011. fi
  1012. }
  1013. # Goes to GS -dColorImageResolution and -dGrayImageResolution parameters
  1014. parseImageResolution() {
  1015. if isNotAnInteger "$1"; then
  1016. printError "Invalid image resolution: $1"
  1017. printError "The image resolution must be an integer"
  1018. exit $EXIT_INVALID_IMAGE_RESOLUTION
  1019. fi
  1020. IMAGE_RESOLUTION="$1"
  1021. }
  1022. # Goes to GS -dColorImageDownsampleType and -dGrayImageDownsampleType parameters
  1023. parseImageDownSample() {
  1024. local param="$(lowercase $1)"
  1025. case "${param}" in
  1026. s|subsample|'/subsample')
  1027. IMAGE_DOWNSAMPLE_TYPE='/Subsample'
  1028. ;;
  1029. a|average|'/average')
  1030. IMAGE_DOWNSAMPLE_TYPE='/Average'
  1031. ;;
  1032. b|bicubic|'/bicubic'|auto)
  1033. IMAGE_DOWNSAMPLE_TYPE='/Bicubic'
  1034. ;;
  1035. *)
  1036. initError "Invalid Image Downsample Mode: \"$1\"" $EXIT_INVALID_OPTION
  1037. return $FALSE
  1038. ;;
  1039. esac
  1040. }
  1041. # Goes to GS -dColorImageDownsampleType and -dGrayImageDownsampleType parameters
  1042. parsePDFSettings() {
  1043. local param="$(lowercase $1)"
  1044. case "${param}" in
  1045. s|screen|'/screen')
  1046. PDF_SETTINGS='/screen'
  1047. ;;
  1048. e|ebook|'/ebook')
  1049. PDF_SETTINGS='/ebook'
  1050. ;;
  1051. p|printer|'/printer'|auto)
  1052. PDF_SETTINGS='/printer'
  1053. ;;
  1054. r|prepress|'/prepress')
  1055. PDF_SETTINGS='/prepress'
  1056. ;;
  1057. d|default|'/default')
  1058. PDF_SETTINGS='/default'
  1059. ;;
  1060. *)
  1061. initError "Invalid PDF Setting Profile: \"$1\""$'\nValid > printer, screen, ebook, prepress, default' $EXIT_INVALID_OPTION
  1062. return $FALSE
  1063. ;;
  1064. esac
  1065. }
  1066. # How to position the resized pages (sets translation)
  1067. parseHorizontalAlignment() {
  1068. local param="$(lowercase $1)"
  1069. case "${param}" in
  1070. l|left)
  1071. HOR_ALIGN='LEFT'
  1072. ;;
  1073. r|right)
  1074. HOR_ALIGN='RIGHT'
  1075. ;;
  1076. c|center|middle)
  1077. HOR_ALIGN='CENTER'
  1078. ;;
  1079. *)
  1080. initError "Invalid Horizontal Alignment Setting: \"$1\""$'\nValid > left, right, center' $EXIT_INVALID_OPTION
  1081. return $FALSE
  1082. ;;
  1083. esac
  1084. }
  1085. # How to position the resized pages (sets translation)
  1086. parseVerticalAlignment() {
  1087. local param="$(lowercase $1)"
  1088. case "${param}" in
  1089. t|top)
  1090. VERT_ALIGN='TOP'
  1091. ;;
  1092. b|bottom|bot)
  1093. VERT_ALIGN='BOTTOM'
  1094. ;;
  1095. c|center|middle)
  1096. VERT_ALIGN='CENTER'
  1097. ;;
  1098. *)
  1099. initError "Invalid Vertical Alignment Setting: \"$1\""$'\nValid > top, bottom, center' $EXIT_INVALID_OPTION
  1100. return $FALSE
  1101. ;;
  1102. esac
  1103. }
  1104. # Set X Translation Offset
  1105. parseXTransOffset() {
  1106. if isFloat "$1"; then
  1107. XTRANSOFFSET="$1"
  1108. return $TRUE
  1109. fi
  1110. printError "Invalid X Translation Offset: $1"
  1111. printError "The X Translation Offset must be a floating point number"
  1112. exit $EXIT_INVALID_OPTION
  1113. }
  1114. # Set Y Translation Offset
  1115. parseYTransOffset() {
  1116. if isFloat "$1"; then
  1117. YTRANSOFFSET="$1"
  1118. return $TRUE
  1119. fi
  1120. printError "Invalid Y Translation Offset: $1"
  1121. printError "The Y Translation Offset must be a floating point number"
  1122. exit $EXIT_INVALID_OPTION
  1123. }
  1124. # Parse Gray Background color
  1125. parseGrayBackground() {
  1126. if isFloatPercentage "$1"; then
  1127. BACKGROUNDCOLOR="$1"
  1128. BACKGROUNDCALL="$BACKGROUNDCOLOR setgray clippath fill " # the space at the end is important!
  1129. BACKGROUNDTYPE="GRAY"
  1130. BACKGROUNDLOG="$GrayColor Mode > $BACKGROUNDCOLOR"
  1131. return $TRUE
  1132. fi
  1133. printError "Invalid Gray Background color."
  1134. printError "Need 1 floating point number between 0 and 1."
  1135. printError "Eg: --background-gray \"0.80\""
  1136. printError "Invalid Param => $1"
  1137. exit $EXIT_INVALID_OPTION
  1138. }
  1139. # Parse CMYK Background color
  1140. parseCMYKBackground() {
  1141. if isFloatPercentage "$1" && isFloatPercentage "$2" && isFloatPercentage "$3" && isFloatPercentage "$4"; then
  1142. BACKGROUNDCOLOR="$1 $2 $3 $4"
  1143. BACKGROUNDCALL="$BACKGROUNDCOLOR setcmykcolor clippath fill " # the space at the end is important!
  1144. BACKGROUNDTYPE="CMYK"
  1145. BACKGROUNDLOG="$BACKGROUNDTYPE Mode > $BACKGROUNDCOLOR"
  1146. return $TRUE
  1147. fi
  1148. printError "Invalid CMYK Background colors."
  1149. printError "Need 4 floating point numbers between 0 and 1 in CMYK order."
  1150. printError "Eg: --background-cmyk \"C M Y K\""
  1151. printError " [C] => $1"
  1152. printError " [M] => $2"
  1153. printError " [Y] => $3"
  1154. printError " [K] => $4"
  1155. exit $EXIT_INVALID_OPTION
  1156. }
  1157. # Just loads the RGB Vars (without testing anything)
  1158. loadRGBVars(){
  1159. local rP="$(rgbToPercentage $1)"
  1160. local gP="$(rgbToPercentage $2)"
  1161. local bP="$(rgbToPercentage $3)"
  1162. BACKGROUNDCOLOR="$rP $gP $bP"
  1163. BACKGROUNDCALL="$BACKGROUNDCOLOR setrgbcolor clippath fill " # the space at the end is important!
  1164. BACKGROUNDTYPE="RGB"
  1165. BACKGROUNDLOG="$BACKGROUNDTYPE Mode > $1($(printf %.2f $rP)) $2($(printf %.2f $gP)) $3($(printf %.2f $bP))"
  1166. }
  1167. # Converts 255-based RGB to Percentage
  1168. rgbToPercentage() {
  1169. local per=$(echo "scale=8; $1 / 255" | "$BCBIN")
  1170. printf '%.7f' "$per" # Print rounded conversion
  1171. }
  1172. # Parse RGB Background color
  1173. parseRGBBackground() {
  1174. if isRGBInteger "$1" && isRGBInteger "$2" && isRGBInteger "$3" ; then
  1175. loadRGBVars "$1" "$2" "$3"
  1176. return $TRUE
  1177. fi
  1178. printError "Invalid RGB Background colors. Need 3 parameters in RGB order."
  1179. printError "Numbers must be RGB integers between 0 and 255."
  1180. printError "Eg: --background-rgb \"34 123 255\""
  1181. printError " [R] => $1"
  1182. printError " [G] => $2"
  1183. printError " [B] => $3"
  1184. exit $EXIT_INVALID_OPTION
  1185. }
  1186. ################### PDF PAGE SIZE DETECTION ####################
  1187. ################################################################
  1188. # Detects operation mode and also runs the adaptive mode
  1189. # PAGESIZE LOGIC
  1190. # 1- Try to get Mediabox with GREP
  1191. # 2- MacOS => try to use mdls
  1192. # 3- Try to use pdfinfo
  1193. # 4- Try to use identify (imagemagick)
  1194. # 5- Fail
  1195. ################################################################
  1196. getPageSize() {
  1197. if isNotAdaptiveMode; then
  1198. vprint " Get Page Size: Adaptive Disabled"
  1199. if [[ $MODE = "CATGREP" ]]; then
  1200. vprint " Method: Grep"
  1201. getPageSizeCatGrep
  1202. elif [[ $MODE = "MDLS" ]]; then
  1203. vprint " Method: Mac Quartz mdls"
  1204. getPageSizeMdls
  1205. elif [[ $MODE = "PDFINFO" ]]; then
  1206. vprint " Method: PDFInfo"
  1207. getPageSizePdfInfo
  1208. elif [[ $MODE = "IDENTIFY" ]]; then
  1209. vprint " Method: ImageMagick's Identify"
  1210. getPageSizeImagemagick
  1211. else
  1212. printError "Error! Invalid Mode: $MODE"
  1213. printError "Aborting execution..."
  1214. exit $EXIT_INVALID_OPTION
  1215. fi
  1216. return $TRUE
  1217. fi
  1218. vprint " Get Page Size: Adaptive Enabled"
  1219. vprint " Method: Grep"
  1220. getPageSizeCatGrep
  1221. if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then
  1222. vprint " Failed"
  1223. vprint " Method: Mac Quartz mdls"
  1224. getPageSizeMdls
  1225. fi
  1226. if pageSizeIsInvalid; then
  1227. vprint " Failed"
  1228. vprint " Method: PDFInfo"
  1229. getPageSizePdfInfo
  1230. fi
  1231. if pageSizeIsInvalid; then
  1232. vprint " Failed"
  1233. vprint " Method: ImageMagick's Identify"
  1234. getPageSizeImagemagick
  1235. fi
  1236. if pageSizeIsInvalid; then
  1237. vprint " Failed"
  1238. printError "Error when detecting PDF paper size!"
  1239. printError "All methods of detection failed"
  1240. printError "You may want to install pdfinfo or imagemagick"
  1241. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  1242. fi
  1243. return $TRUE
  1244. }
  1245. # Gets page size using imagemagick's identify
  1246. getPageSizeImagemagick() {
  1247. # Sanity and Adaptive together
  1248. if isNotFile "$IDBIN" && isNotAdaptiveMode; then
  1249. notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify"
  1250. elif isNotFile "$IDBIN" && isAdaptiveMode; then
  1251. return $FALSE
  1252. fi
  1253. # get data from image magick
  1254. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  1255. if isEmpty "$identify" && isNotAdaptiveMode; then
  1256. notAdaptiveFailed "ImageMagicks's Identify returned an empty string!"
  1257. elif isEmpty "$identify" && isAdaptiveMode; then
  1258. return $FALSE
  1259. fi
  1260. identify="${identify%%BREAKME*}" # get page size only for 1st page
  1261. identify=($identify) # make it an array
  1262. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  1263. PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign
  1264. return $TRUE
  1265. }
  1266. # Gets page size using Mac Quarts mdls
  1267. getPageSizeMdls() {
  1268. # Sanity and Adaptive together
  1269. if isNotFile "$MDLSBIN" && isNotAdaptiveMode; then
  1270. notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls"
  1271. elif isNotFile "$MDLSBIN" && isAdaptiveMode; then
  1272. return $FALSE
  1273. fi
  1274. local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)"
  1275. if isEmpty "$identify" && isNotAdaptiveMode; then
  1276. notAdaptiveFailed "Mac Quartz mdls returned an empty string!"
  1277. elif isEmpty "$identify" && isAdaptiveMode; then
  1278. return $FALSE
  1279. fi
  1280. identify=${identify//$'\t'/ } # change tab to space
  1281. identify=($identify) # make it an array
  1282. if [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isNotAdaptiveMode; then
  1283. notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?"
  1284. elif [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isAdaptiveMode; then
  1285. return $FALSE
  1286. fi
  1287. PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign
  1288. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  1289. return $TRUE
  1290. }
  1291. # Gets page size using Linux PdfInfo
  1292. getPageSizePdfInfo() {
  1293. # Sanity and Adaptive together
  1294. if isNotFile "$PDFINFOBIN" && isNotAdaptiveMode; then
  1295. notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo"
  1296. elif isNotFile "$PDFINFOBIN" && isAdaptiveMode; then
  1297. return $FALSE
  1298. fi
  1299. # get data from image magick
  1300. local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | "$GREPBIN" -i 'Page size:' )"
  1301. if isEmpty "$identify" && isNotAdaptiveMode; then
  1302. notAdaptiveFailed "Linux PdfInfo returned an empty string!"
  1303. elif isEmpty "$identify" && isAdaptiveMode; then
  1304. return $FALSE
  1305. fi
  1306. identify="${identify##*Page size:}" # remove stuff
  1307. identify=($identify) # make it an array
  1308. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  1309. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  1310. return $TRUE
  1311. }
  1312. # Gets page size using cat and grep
  1313. getPageSizeCatGrep() {
  1314. # get MediaBox info from PDF file using grep, these are all possible
  1315. # /MediaBox [0 0 595 841]
  1316. # /MediaBox [ 0 0 595.28 841.89]
  1317. # /MediaBox[ 0 0 595.28 841.89 ]
  1318. # Get MediaBox data if possible
  1319. local mediaBox="$("$GREPBIN" -a -e '/MediaBox' -m 1 "$INFILEPDF" 2>/dev/null)"
  1320. mediaBox="${mediaBox##*/MediaBox}"
  1321. mediaBox="${mediaBox##*[}"
  1322. mediaBox="${mediaBox%%]*}"
  1323. #echo "mediaBox=$mediaBox"
  1324. # No page size data available
  1325. if isEmpty "$mediaBox" && isNotAdaptiveMode; then
  1326. notAdaptiveFailed "There is no MediaBox in the pdf document!"
  1327. elif isEmpty "$mediaBox" && isAdaptiveMode; then
  1328. return $FALSE
  1329. fi
  1330. mediaBox=($mediaBox) # make it an array
  1331. mbCount=${#mediaBox[@]} # array size
  1332. # sanity
  1333. if [[ $mbCount -lt 4 ]] || ! isFloat "${mediaBox[2]}" || ! isFloat "${mediaBox[3]}" || isZero "${mediaBox[2]}" || isZero "${mediaBox[3]}"; then
  1334. if isNotAdaptiveMode; then
  1335. notAdaptiveFailed $'Error when reading the page size!\nThe page size information is invalid!'
  1336. fi
  1337. return $FALSE
  1338. fi
  1339. # we are done
  1340. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  1341. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  1342. #echo "PGWIDTH=$PGWIDTH // PGHEIGHT=$PGHEIGHT"
  1343. return $TRUE
  1344. }
  1345. isZero() {
  1346. [[ "$1" == "0" ]] && return $TRUE
  1347. [[ "$1" == "0.0" ]] && return $TRUE
  1348. [[ "$1" == "0.00" ]] && return $TRUE
  1349. [[ "$1" == "0.000" ]] && return $TRUE
  1350. return $FALSE
  1351. }
  1352. # Prints error message and exits execution
  1353. notAdaptiveFailed() {
  1354. local errProgram="$2"
  1355. local errStr="$1"
  1356. if isEmpty "$2"; then
  1357. printError "Error when reading input file!"
  1358. printError "Could not determine the page size!"
  1359. else
  1360. printError "Error! $2 was not found!"
  1361. fi
  1362. isNotEmpty "$errStr" && printError "$errStr"
  1363. printError "Aborting! You may want to try the adaptive mode."
  1364. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  1365. }
  1366. # Verbose print of the Width and Height (Source or New) to screen
  1367. vPrintPageSizes() {
  1368. vprint " $1 Width: $PGWIDTH postscript-points"
  1369. vprint "$1 Height: $PGHEIGHT postscript-points"
  1370. }
  1371. #################### GHOSTSCRIPT PAPER INFO ####################
  1372. # Loads valid paper info to memory
  1373. getPaperInfo() {
  1374. # name inchesW inchesH mmW mmH pointsW pointsH
  1375. sizesUS="\
  1376. 11x17 11.0 17.0 279 432 792 1224
  1377. ledger 17.0 11.0 432 279 1224 792
  1378. legal 8.5 14.0 216 356 612 1008
  1379. letter 8.5 11.0 216 279 612 792
  1380. lettersmall 8.5 11.0 216 279 612 792
  1381. archE 36.0 48.0 914 1219 2592 3456
  1382. archD 24.0 36.0 610 914 1728 2592
  1383. archC 18.0 24.0 457 610 1296 1728
  1384. archB 12.0 18.0 305 457 864 1296
  1385. archA 9.0 12.0 229 305 648 864"
  1386. sizesISO="\
  1387. a0 33.1 46.8 841 1189 2384 3370
  1388. a1 23.4 33.1 594 841 1684 2384
  1389. a2 16.5 23.4 420 594 1191 1684
  1390. a3 11.7 16.5 297 420 842 1191
  1391. a4 8.3 11.7 210 297 595 842
  1392. a4small 8.3 11.7 210 297 595 842
  1393. a5 5.8 8.3 148 210 420 595
  1394. a6 4.1 5.8 105 148 297 420
  1395. a7 2.9 4.1 74 105 210 297
  1396. a8 2.1 2.9 52 74 148 210
  1397. a9 1.5 2.1 37 52 105 148
  1398. a10 1.0 1.5 26 37 73 105
  1399. isob0 39.4 55.7 1000 1414 2835 4008
  1400. isob1 27.8 39.4 707 1000 2004 2835
  1401. isob2 19.7 27.8 500 707 1417 2004
  1402. isob3 13.9 19.7 353 500 1001 1417
  1403. isob4 9.8 13.9 250 353 709 1001
  1404. isob5 6.9 9.8 176 250 499 709
  1405. isob6 4.9 6.9 125 176 354 499
  1406. c0 36.1 51.1 917 1297 2599 3677
  1407. c1 25.5 36.1 648 917 1837 2599
  1408. c2 18.0 25.5 458 648 1298 1837
  1409. c3 12.8 18.0 324 458 918 1298
  1410. c4 9.0 12.8 229 324 649 918
  1411. c5 6.4 9.0 162 229 459 649
  1412. c6 4.5 6.4 114 162 323 459"
  1413. sizesJIS="\
  1414. jisb0 NA NA 1030 1456 2920 4127
  1415. jisb1 NA NA 728 1030 2064 2920
  1416. jisb2 NA NA 515 728 1460 2064
  1417. jisb3 NA NA 364 515 1032 1460
  1418. jisb4 NA NA 257 364 729 1032
  1419. jisb5 NA NA 182 257 516 729
  1420. jisb6 NA NA 128 182 363 516"
  1421. sizesOther="\
  1422. flsa 8.5 13.0 216 330 612 936
  1423. flse 8.5 13.0 216 330 612 936
  1424. halfletter 5.5 8.5 140 216 396 612
  1425. hagaki 3.9 5.8 100 148 283 420"
  1426. sizesAll="\
  1427. $sizesUS
  1428. $sizesISO
  1429. $sizesJIS
  1430. $sizesOther"
  1431. }
  1432. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  1433. getGSPaperSize() {
  1434. isEmpty "$sizesall" && getPaperInfo
  1435. while read l; do
  1436. local cols=($l)
  1437. if [[ "$1" == ${cols[0]} ]]; then
  1438. RESIZE_WIDTH=${cols[5]}
  1439. RESIZE_HEIGHT=${cols[6]}
  1440. return $TRUE
  1441. fi
  1442. done <<< "$sizesAll"
  1443. }
  1444. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  1445. getGSPaperName() {
  1446. local w="$(printf "%.0f" $1)"
  1447. local h="$(printf "%.0f" $2)"
  1448. isEmpty "$sizesall" && getPaperInfo
  1449. # Because US Standard has inverted sizes, I need to scan 2 times
  1450. # instead of just testing if width is bigger than height
  1451. while read l; do
  1452. local cols=($l)
  1453. if [[ "$w" == ${cols[5]} && "$h" == ${cols[6]} ]]; then
  1454. printf "%s Portrait" $(uppercase ${cols[0]})
  1455. return $TRUE
  1456. fi
  1457. done <<< "$sizesAll"
  1458. while read l; do
  1459. local cols=($l)
  1460. if [[ "$w" == ${cols[6]} && "$h" == ${cols[5]} ]]; then
  1461. printf "%s Landscape" $(uppercase ${cols[0]})
  1462. return $TRUE
  1463. fi
  1464. done <<< "$sizesAll"
  1465. return $FALSE
  1466. }
  1467. # Loads an array with paper names to memory
  1468. getPaperNames() {
  1469. 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 \
  1470. 11x17 ledger legal letter lettersmall archE archD archC archB archA \
  1471. jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \
  1472. flsa flse halfletter hagaki)
  1473. }
  1474. # Prints uppercase paper names to screen (used in help)
  1475. printPaperNames() {
  1476. isEmpty "$paperNames" && getPaperNames
  1477. for i in "${!paperNames[@]}"; do
  1478. [[ $i -eq 0 ]] && echo -n -e ' '
  1479. [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo -n -e $'\n '
  1480. ppN="$(uppercase ${paperNames[i]})"
  1481. printf "%-14s" "$ppN"
  1482. done
  1483. echo ""
  1484. }
  1485. # Returns $TRUE if $! is a valid paper name, $FALSE otherwise
  1486. isPaperName() {
  1487. isEmpty "$1" && return $FALSE
  1488. isEmpty "$paperNames" && getPaperNames
  1489. for i in "${paperNames[@]}"; do
  1490. [[ "$i" = "$1" ]] && return $TRUE
  1491. done
  1492. return $FALSE
  1493. }
  1494. # Prints all tables with ghostscript paper information
  1495. printPaperInfo() {
  1496. printVersion 3
  1497. echo $'\n'"Paper Sizes Information"$'\n'
  1498. getPaperInfo
  1499. printPaperTable "ISO STANDARD" "$sizesISO"; echo
  1500. printPaperTable "US STANDARD" "$sizesUS"; echo
  1501. printPaperTable "JIS STANDARD *Aproximated Points" "$sizesJIS"; echo
  1502. printPaperTable "OTHERS" "$sizesOther"; echo
  1503. }
  1504. # GS paper table helper, prints a full line
  1505. printTableLine() {
  1506. echo '+-----------------------------------------------------------------+'
  1507. }
  1508. # GS paper table helper, prints a line with dividers
  1509. printTableDivider() {
  1510. echo '+-----------------+-------+-------+-------+-------+-------+-------+'
  1511. }
  1512. # GS paper table helper, prints a table header
  1513. printTableHeader() {
  1514. echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |'
  1515. }
  1516. # GS paper table helper, prints a table title
  1517. printTableTitle() {
  1518. printf "| %-64s%s\n" "$1" '|'
  1519. }
  1520. # GS paper table printer, prints a table for a paper variable
  1521. printPaperTable() {
  1522. printTableLine
  1523. printTableTitle "$1"
  1524. printTableLine
  1525. printTableHeader
  1526. printTableDivider
  1527. while read l; do
  1528. local cols=($l)
  1529. printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]};
  1530. done <<< "$2"
  1531. printTableDivider
  1532. }
  1533. # Returns $TRUE if $1 is a valid measurement for a custom paper, $FALSE otherwise
  1534. isNotValidMeasure() {
  1535. isMilimeter "$1" || isInch "$1" || isPoint "$1" && return $FALSE
  1536. return $TRUE
  1537. }
  1538. # Returns $TRUE if $1 is a valid milimeter string, $FALSE otherwise
  1539. isMilimeter() {
  1540. [[ "$1" = 'mm' || "$1" = 'milimeters' || "$1" = 'milimeter' ]] && return $TRUE
  1541. return $FALSE
  1542. }
  1543. # Returns $TRUE if $1 is a valid inch string, $FALSE otherwise
  1544. isInch() {
  1545. [[ "$1" = 'in' || "$1" = 'inch' || "$1" = 'inches' ]] && return $TRUE
  1546. return $FALSE
  1547. }
  1548. # Returns $TRUE if $1 is a valid point string, $FALSE otherwise
  1549. isPoint() {
  1550. [[ "$1" = 'pt' || "$1" = 'pts' || "$1" = 'point' || "$1" = 'points' ]] && return $TRUE
  1551. return $FALSE
  1552. }
  1553. # Returns $TRUE if a custom paper is being used, $FALSE otherwise
  1554. isCustomPaper() {
  1555. return $CUSTOM_RESIZE_PAPER
  1556. }
  1557. # Returns $FALSE if a custom paper is being used, $TRUE otherwise
  1558. isNotCustomPaper() {
  1559. isCustomPaper && return $FALSE
  1560. return $TRUE
  1561. }
  1562. ######################### CONVERSIONS ##########################
  1563. # Prints the lowercase char value for $1
  1564. lowercaseChar() {
  1565. case "$1" in
  1566. [A-Z])
  1567. n=$(printf "%d" "'$1")
  1568. n=$((n+32))
  1569. printf \\$(printf "%o" "$n")
  1570. ;;
  1571. *)
  1572. printf "%s" "$1"
  1573. ;;
  1574. esac
  1575. }
  1576. # Prints the lowercase version of a string
  1577. lowercase() {
  1578. word="$@"
  1579. for((i=0;i<${#word};i++))
  1580. do
  1581. ch="${word:$i:1}"
  1582. lowercaseChar "$ch"
  1583. done
  1584. }
  1585. # Prints the uppercase char value for $1
  1586. uppercaseChar(){
  1587. case "$1" in
  1588. [a-z])
  1589. n=$(printf "%d" "'$1")
  1590. n=$((n-32))
  1591. printf \\$(printf "%o" "$n")
  1592. ;;
  1593. *)
  1594. printf "%s" "$1"
  1595. ;;
  1596. esac
  1597. }
  1598. # Prints the uppercase version of a string
  1599. uppercase() {
  1600. word="$@"
  1601. for((i=0;i<${#word};i++))
  1602. do
  1603. ch="${word:$i:1}"
  1604. uppercaseChar "$ch"
  1605. done
  1606. }
  1607. # Prints the postscript points rounded equivalent from $1 mm
  1608. milimetersToPoints() {
  1609. local pts=$(echo "scale=8; $1 * 72 / 25.4" | "$BCBIN")
  1610. printf '%.0f' "$pts" # Print rounded conversion
  1611. }
  1612. # Prints the postscript points rounded equivalent from $1 inches
  1613. inchesToPoints() {
  1614. local pts=$(echo "scale=8; $1 * 72" | "$BCBIN")
  1615. printf '%.0f' "$pts" # Print rounded conversion
  1616. }
  1617. # Prints the mm equivalent from $1 postscript points
  1618. pointsToMilimeters() {
  1619. local pts=$(echo "scale=8; $1 / 72 * 25.4" | "$BCBIN")
  1620. printf '%.0f' "$pts" # Print rounded conversion
  1621. }
  1622. # Prints the inches equivalent from $1 postscript points
  1623. pointsToInches() {
  1624. local pts=$(echo "scale=8; $1 / 72" | "$BCBIN")
  1625. printf '%.1f' "$pts" # Print rounded conversion
  1626. }
  1627. ######################## MODE-DETECTION ########################
  1628. # Returns $TRUE if the scale was set manually, $FALSE if we are using automatic scaling
  1629. isManualScaledMode() {
  1630. [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE
  1631. return $TRUE
  1632. }
  1633. # Returns true if we are resizing a paper (ignores scaling), false otherwise
  1634. isResizeMode() {
  1635. isEmpty $RESIZE_PAPER_TYPE && return $FALSE
  1636. return $TRUE
  1637. }
  1638. # Returns true if we are resizing a paper and the scale was manually set
  1639. isMixedMode() {
  1640. isResizeMode && isManualScaledMode && return $TRUE
  1641. return $FALSE
  1642. }
  1643. # Return $TRUE if adaptive mode is enabled, $FALSE otherwise
  1644. isAdaptiveMode() {
  1645. return $ADAPTIVEMODE
  1646. }
  1647. # Return $TRUE if adaptive mode is disabled, $FALSE otherwise
  1648. isNotAdaptiveMode() {
  1649. isAdaptiveMode && return $FALSE
  1650. return $TRUE
  1651. }
  1652. ########################## VALIDATORS ##########################
  1653. # Returns $TRUE if we don't need to create a background
  1654. noBackground() {
  1655. [[ "$BACKGROUNDTYPE" == "CMYK" ]] && return $FALSE
  1656. [[ "$BACKGROUNDTYPE" == "RGB" ]] && return $FALSE
  1657. [[ "$BACKGROUNDTYPE" == "GRAY" ]] && return $FALSE
  1658. return $TRUE
  1659. }
  1660. # Returns $TRUE if we need to create a background
  1661. hasBackground() {
  1662. [[ "$BACKGROUNDTYPE" == "CMYK" ]] && return $TRUE
  1663. [[ "$BACKGROUNDTYPE" == "RGB" ]] && return $TRUE
  1664. [[ "$BACKGROUNDTYPE" == "GRAY" ]] && return $TRUE
  1665. return $FALSE
  1666. }
  1667. # Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, $FALSE otherwise
  1668. pageSizeIsInvalid() {
  1669. if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then
  1670. return $TRUE
  1671. fi
  1672. return $FALSE
  1673. }
  1674. # Return $TRUE if $1 is empty, $FALSE otherwise
  1675. isEmpty() {
  1676. [[ -z "$1" ]] && return $TRUE
  1677. return $FALSE
  1678. }
  1679. # Return $TRUE if $1 is NOT empty, $FALSE otherwise
  1680. isNotEmpty() {
  1681. [[ -z "$1" ]] && return $FALSE
  1682. return $TRUE
  1683. }
  1684. # Returns $TRUE if $1 is an integer, $FALSE otherwise
  1685. isAnInteger() {
  1686. case $1 in
  1687. ''|*[!0-9]*) return $FALSE ;;
  1688. *) return $TRUE ;;
  1689. esac
  1690. }
  1691. # Returns $TRUE if $1 is NOT an integer, $FALSE otherwise
  1692. isNotAnInteger() {
  1693. case $1 in
  1694. ''|*[!0-9]*) return $TRUE ;;
  1695. *) return $FALSE ;;
  1696. esac
  1697. }
  1698. # Returns $TRUE if $1 is an integer, $FALSE otherwise
  1699. isRGBInteger() {
  1700. isAnInteger "$1" && [[ $1 -ge 0 ]] && [[ $1 -le 255 ]] && return $TRUE
  1701. return $FALSE
  1702. }
  1703. # Returns $TRUE if $1 is a floating point number (or an integer), $FALSE otherwise
  1704. isFloat() {
  1705. [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ ]] && return $TRUE
  1706. return $FALSE
  1707. }
  1708. # Returns $TRUE if $1 is a floating point number bigger than zero, $FALSE otherwise
  1709. isFloatBiggerThanZero() {
  1710. isFloat "$1" && [[ (( $1 > 0 )) ]] && return $TRUE
  1711. return $FALSE
  1712. }
  1713. # Returns $TRUE if $1 is a floating point number between 0 and 1, $FALSE otherwise
  1714. isFloatPercentage() {
  1715. [[ -n "$1" && "$1" =~ ^-?[0]*([.][0-9]+)?$ ]] && return $TRUE
  1716. [[ "$1" == "1" ]] && return $TRUE
  1717. return $FALSE
  1718. }
  1719. # Returns $TRUE if $1 is readable, $FALSE otherwise
  1720. isReadable() {
  1721. [[ -r "$1" ]] && return $TRUE
  1722. return $FALSE;
  1723. }
  1724. # Returns $TRUE if $1 is a directory, $FALSE otherwise
  1725. isDir() {
  1726. [[ -d "$1" ]] && return $TRUE
  1727. return $FALSE;
  1728. }
  1729. # Returns $FALSE if $1 is a directory, $TRUE otherwise
  1730. isNotDir() {
  1731. isDir "$1" && return $FALSE
  1732. return $TRUE;
  1733. }
  1734. # Returns 0 if succeded, other integer otherwise
  1735. isTouchable() {
  1736. touch "$1" 2>/dev/null
  1737. }
  1738. # Returns $TRUE if $1 has a .pdf extension, false otherwsie
  1739. isPDF() {
  1740. [[ "$(lowercase $1)" =~ ^..*\.pdf$ ]] && return $TRUE
  1741. return $FALSE
  1742. }
  1743. # Returns $TRUE if $1 is a file, false otherwsie
  1744. isFile() {
  1745. [[ -f "$1" ]] && return $TRUE
  1746. return $FALSE
  1747. }
  1748. # Returns $TRUE if $1 is NOT a file, false otherwsie
  1749. isNotFile() {
  1750. [[ -f "$1" ]] && return $FALSE
  1751. return $TRUE
  1752. }
  1753. # Returns $TRUE if $1 is executable, false otherwsie
  1754. isExecutable() {
  1755. [[ -x "$1" ]] && return $TRUE
  1756. return $FALSE
  1757. }
  1758. # Returns $TRUE if $1 is NOT executable, false otherwsie
  1759. isNotExecutable() {
  1760. [[ -x "$1" ]] && return $FALSE
  1761. return $TRUE
  1762. }
  1763. # Returns $TRUE if $1 is a file and executable, false otherwsie
  1764. isAvailable() {
  1765. if isFile "$1" && isExecutable "$1"; then
  1766. return $TRUE
  1767. fi
  1768. return $FALSE
  1769. }
  1770. # Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie
  1771. isNotAvailable() {
  1772. if isNotFile "$1" || isNotExecutable "$1"; then
  1773. return $TRUE
  1774. fi
  1775. return $FALSE
  1776. }
  1777. # Returns $TRUE if we should avoid https certificate (on upgrade)
  1778. useInsecure() {
  1779. return $HTTPS_INSECURE
  1780. }
  1781. # Returns $TRUE if we should not ask anything and assume yes as answer
  1782. assumeYes() {
  1783. return $ASSUME_YES
  1784. }
  1785. # Returns $TRUE if we should ask the user for input
  1786. shouldAskUser() {
  1787. assumeYes && return $FALSE
  1788. return $TRUE
  1789. }
  1790. ###################### PRINTING TO SCREEN ######################
  1791. # Prints version
  1792. printVersion() {
  1793. local vStr=""
  1794. [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution"
  1795. local strBanner="$PDFSCALE_NAME v$VERSION$vStr"
  1796. if [[ $1 -eq 2 ]]; then
  1797. printError "$strBanner"
  1798. elif [[ $1 -eq 3 ]]; then
  1799. local extra="$(isNotEmpty "$2" && echo "$2")"
  1800. echo "$strBanner$extra"
  1801. else
  1802. vprint "$strBanner"
  1803. fi
  1804. }
  1805. # Prints input, output file info, if verbosing
  1806. vPrintFileInfo() {
  1807. vprint " Input File: $INFILEPDF"
  1808. vprint " Output File: $OUTFILEPDF"
  1809. }
  1810. # Prints the scale factor to screen, or custom message
  1811. vPrintScaleFactor() {
  1812. local scaleMsg="$SCALE"
  1813. isNotEmpty "$1" && scaleMsg="$1"
  1814. vprint " Scale Factor: $scaleMsg"
  1815. }
  1816. # Prints help info
  1817. printHelp() {
  1818. printVersion 3
  1819. local paperList="$(printPaperNames)"
  1820. echo "
  1821. Usage: $PDFSCALE_NAME <inFile.pdf>
  1822. $PDFSCALE_NAME -i <inFile.pdf>
  1823. $PDFSCALE_NAME [-v] [-s <factor>] [-m <page-detection>] <inFile.pdf> [outfile.pdf]
  1824. $PDFSCALE_NAME [-v] [-r <paper>] [-f <flip-detection>] [-a <auto-rotation>] <inFile.pdf> [outfile.pdf]
  1825. $PDFSCALE_NAME -p
  1826. $PDFSCALE_NAME -h
  1827. $PDFSCALE_NAME -V
  1828. Parameters:
  1829. -v, --verbose
  1830. Verbose mode, prints extra information
  1831. Use twice for timestamp
  1832. -h, --help
  1833. Print this help to screen and exits
  1834. -V, --version
  1835. Prints version to screen and exits
  1836. --install, --self-install [target-path]
  1837. Install itself to [target-path] or /usr/local/bin/pdfscale if not specified
  1838. Should contain the full path with the desired executable name
  1839. --upgrade, --self-upgrade
  1840. Upgrades itself in-place (same path/name of the pdfScale.sh caller)
  1841. Downloads the master branch tarball and tries to self-upgrade
  1842. --insecure, --no-check-certificate
  1843. Use curl/wget without SSL library support
  1844. --yes, --assume-yes
  1845. Will answer yes to any prompt on install or upgrade, use with care
  1846. -n, --no-overwrite
  1847. Aborts execution if the output PDF file already exists
  1848. By default, the output file will be overwritten
  1849. -m, --mode <mode>
  1850. Paper size detection mode
  1851. Modes: a, adaptive Default mode, tries all the methods below
  1852. g, grep Forces the use of Grep method
  1853. m, mdls Forces the use of MacOS Quartz mdls
  1854. p, pdfinfo Forces the use of PDFInfo
  1855. i, identify Forces the use of ImageMagick's Identify
  1856. -i, --info <file>
  1857. Prints <file> Paper Size information to screen and exits
  1858. -s, --scale <factor>
  1859. Changes the scaling factor or forces mixed mode
  1860. Defaults: $SCALE (scale mode) / Disabled (resize mode)
  1861. MUST be a number bigger than zero
  1862. Eg. -s 0.8 for 80% of the original size
  1863. -r, --resize <paper>
  1864. Triggers the Resize Paper Mode, disables auto-scaling of $SCALE
  1865. Resize PDF and fit-to-page
  1866. <paper> can be: source, custom or a valid std paper name, read below
  1867. -f, --flip-detect <mode>
  1868. Flip Detection Mode, defaults to 'auto'
  1869. Inverts Width <-> Height of a Resized PDF
  1870. Modes: a, auto Keeps source orientation, default
  1871. f, force Forces flip W <-> H
  1872. d, disable Disables flipping
  1873. -a, --auto-rotate <mode>
  1874. Setting for GS -dAutoRotatePages, defaults to 'PageByPage'
  1875. Uses text-orientation detection to set Portrait/Landscape
  1876. Modes: p, pagebypage Auto-rotates pages individually
  1877. n, none Retains orientation of each page
  1878. a, all Rotates all pages (or none) depending
  1879. on a kind of \"majority decision\"
  1880. --hor-align, --horizontal-alignment <left|center|right>
  1881. Where to translate the scaled page
  1882. Default: center
  1883. Options: left, right, center
  1884. --vert-align, --vertical-alignment <top|center|bottom>
  1885. Where to translate the scaled page
  1886. Default: center
  1887. Options: top, bottom, center
  1888. --xoffset, --xtrans-offset <FloatNumber>
  1889. Add/Subtract from the X translation (move left-right)
  1890. Default: 0.0 (zero)
  1891. Options: Positive or negative floating point number
  1892. --yoffset, --ytrans-offset <FloatNumber>
  1893. Add/Subtract from the Y translation (move top-bottom)
  1894. Default: 0.0 (zero)
  1895. Options: Positive or negative floating point number
  1896. --pdf-settings <gs-pdf-profile>
  1897. Ghostscript PDF Profile to use in -dPDFSETTINGS
  1898. Default: printer
  1899. Options: screen, ebook, printer, prepress, default
  1900. --image-downsample <gs-downsample-method>
  1901. Ghostscript Image Downsample Method
  1902. Default: bicubic
  1903. Options: subsample, average, bicubic
  1904. --image-resolution <dpi>
  1905. Resolution in DPI of color and grayscale images in output
  1906. Default: 300
  1907. --background-gray <percentage>
  1908. Creates a background with a gray color setting
  1909. Percentage is a floating point percentage number between 0(black) and 1(white)
  1910. --background-cmyk <\"C M Y K\">
  1911. Creates a background with a CMYK color setting
  1912. Must be quoted into a single parameter as in \"0.2 0.2 0.2 0.2\"
  1913. Each color parameter is a floating point percentage number (between 0 and 1)
  1914. --background-rgb <\"R G B\">
  1915. Creates a background with a RGB color setting
  1916. Must be quoted into a single parameter as in \"100 100 200\"
  1917. RGB numbers are integers between 0 and 255 (255 122 50)
  1918. --dry-run, --simulate
  1919. Just simulate execution. Will not run ghostscript
  1920. --print-gs-call, --gs-call
  1921. Print GS call to stdout. Will print at the very end between markers
  1922. -p, --print-papers
  1923. Prints Standard Paper info tables to screen and exits
  1924. Scaling Mode:
  1925. - The default mode of operation is scaling mode with fixed paper
  1926. size and scaling pre-set to $SCALE
  1927. - By not using the resize mode you are using scaling mode
  1928. - Flip-Detection and Auto-Rotation are disabled in Scaling mode,
  1929. you can use '-r source -s <scale>' to override.
  1930. - Ghostscript placement is from bottom-left position. This means that
  1931. a bottom-left placement has ZERO for both X and Y translations.
  1932. Resize Paper Mode:
  1933. - Disables the default scaling factor! ($SCALE)
  1934. - Changes the PDF Paper Size in points. Will fit-to-page
  1935. Mixed Mode:
  1936. - In mixed mode both the -s option and -r option must be specified
  1937. - The PDF will be first resized then scaled
  1938. Output filename:
  1939. - Having the extension .pdf on the output file name is optional,
  1940. it will be added if not present.
  1941. - The output filename is optional. If no file name is passed
  1942. the output file will have the same name/destination of the
  1943. input file with added suffixes:
  1944. .SCALED.pdf is added to scaled files
  1945. .<PAPERSIZE>.pdf is added to resized files
  1946. .<PAPERSIZE>.SCALED.pdf is added in mixed mode
  1947. Standard Paper Names: (case-insensitive)
  1948. $paperList
  1949. Custom Paper Size:
  1950. - Paper size can be set manually in Milimeters, Inches or Points
  1951. - Custom paper definition MUST be quoted into a single parameter
  1952. - Actual size is applied in points (mms and inches are transformed)
  1953. - Measurements: mm, mms, milimeters
  1954. pt, pts, points
  1955. in, inch, inches
  1956. Use: $PDFSCALE_NAME -r 'custom <measurement> <width> <height>'
  1957. Ex: $PDFSCALE_NAME -r 'custom mm 300 300'
  1958. Using Source Paper Size: (no-resizing)
  1959. - Wildcard 'source' is used used to keep paper size the same as the input
  1960. - Usefull to run Auto-Rotation without resizing
  1961. - Eg. $PDFSCALE_NAME -r source ./input.pdf
  1962. Options and Parameters Parsing:
  1963. - From v2.1.0 (long-opts) there is no need to pass file names at the end
  1964. - Anything that is not a short-option is case-insensitive
  1965. - Short-options: case-sensitive Eg. -v for Verbose, -V for Version
  1966. - Long-options: case-insensitive Eg. --SCALE and --scale are the same
  1967. - Subparameters: case-insensitive Eg. -m PdFinFo is valid
  1968. - Grouping short-options is not supported Eg. -vv, or -vs 0.9
  1969. Additional Notes:
  1970. - File and folder names with spaces should be quoted or escaped
  1971. - Using a scale bigger than 1.0 may result on cropping parts of the PDF
  1972. - For detailed paper types information, use: $PDFSCALE_NAME -p
  1973. Examples:
  1974. $PDFSCALE_NAME myPdfFile.pdf
  1975. $PDFSCALE_NAME -i '/home/My Folder/My PDF File.pdf'
  1976. $PDFSCALE_NAME myPdfFile.pdf \"My Scaled Pdf\"
  1977. $PDFSCALE_NAME -v -v myPdfFile.pdf
  1978. $PDFSCALE_NAME -s 0.85 myPdfFile.pdf My\\ Scaled\\ Pdf.pdf
  1979. $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf
  1980. $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf
  1981. $PDFSCALE_NAME -r A4 myPdfFile.pdf
  1982. $PDFSCALE_NAME -v -v -r \"custom mm 252 356\" -s 0.9 -f \"../input file.pdf\" \"../my new pdf\"
  1983. "
  1984. }
  1985. # Prints usage info
  1986. usage() {
  1987. [[ "$2" != 'nobanner' ]] && printVersion 2
  1988. isNotEmpty "$1" && printError $'\n'"$1"
  1989. printError $'\n'"Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] [-r <paper> [-f <mode>] [-a <mode>]] <inFile.pdf> [outfile.pdf]"
  1990. printError "Help : $PDFSCALE_NAME -h"
  1991. }
  1992. # Prints Verbose information
  1993. vprint() {
  1994. [[ $VERBOSE -eq 0 ]] && return $TRUE
  1995. timestamp=""
  1996. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  1997. echo "$timestamp$1"
  1998. }
  1999. # Prints dependency information and aborts execution
  2000. printDependency() {
  2001. printVersion 2
  2002. local brewName="$1"
  2003. [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf"
  2004. printError $'\n'"ERROR! You need to install the package '$1'"$'\n'
  2005. printError "Linux apt-get.: sudo apt-get install $1"
  2006. printError "Linux yum.....: sudo yum install $1"
  2007. printError "MacOS homebrew: brew install $brewName"
  2008. printError $'\n'"Aborting..."
  2009. exit $EXIT_MISSING_DEPENDENCY
  2010. }
  2011. # Prints initialization errors and aborts execution
  2012. initError() {
  2013. local errStr="$1"
  2014. local exitStat=$2
  2015. isEmpty "$exitStat" && exitStat=$EXIT_ERROR
  2016. usage "ERROR! $errStr" "$3"
  2017. exit $exitStat
  2018. }
  2019. # Prints to stderr
  2020. printError() {
  2021. echo >&2 "$@"
  2022. }
  2023. ########################## EXECUTION ###########################
  2024. initDeps
  2025. getOptions "${@}"
  2026. main
  2027. exit $?