Bash Script to scale and/or resize PDFs from the command line.
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

1443 linhas
50 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 - 2017 / 05 / 19
  8. #
  9. # This script: https://github.com/tavinus/pdfScale
  10. # Based on: http://ma.juii.net/blog/scale-page-content-of-pdf-files
  11. # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a
  12. VERSION="2.1.3"
  13. ###################### EXTERNAL PROGRAMS #######################
  14. GSBIN="" # GhostScript Binary
  15. BCBIN="" # BC Math Binary
  16. IDBIN="" # Identify Binary
  17. PDFINFOBIN="" # PDF Info Binary
  18. MDLSBIN="" # MacOS mdls Binary
  19. ##################### ENVIRONMENT SET-UP #######################
  20. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  21. LC_ALL="C" # Some languages use , as decimal token
  22. LC_CTYPE="C"
  23. LC_NUMERIC="C"
  24. TRUE=0 # Silly stuff
  25. FALSE=1
  26. ########################### GLOBALS ############################
  27. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  28. VERBOSE=0 # verbosity Level
  29. PDFSCALE_NAME="$(basename $0)" # simplified name of this script
  30. OSNAME="$(uname 2>/dev/null)" # Check where we are running
  31. GS_RUN_STATUS="" # Holds GS error messages, signals errors
  32. INFILEPDF="" # Input PDF file name
  33. OUTFILEPDF="" # Output PDF file name
  34. JUST_IDENTIFY=$FALSE # Flag to just show PDF info
  35. ABORT_ON_OVERWRITE=$FALSE # Flag to abort if OUTFILEPDF already exists
  36. ADAPTIVEMODE=$TRUE # Automatically try to guess best mode
  37. AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, disabled in resize mode
  38. MODE="" # Which page size detection to use
  39. RESIZE_PAPER_TYPE="" # Pre-defined paper to use
  40. CUSTOM_RESIZE_PAPER=$FALSE # If we are using a custom-defined paper
  41. FLIP_DETECTION=$TRUE # If we should run the Flip-detection
  42. FLIP_FORCE=$FALSE # If we should force Flipping
  43. AUTO_ROTATION='/PageByPage' # GS call auto-rotation setting
  44. PGWIDTH="" # Input PDF Page Width
  45. PGHEIGHT="" # Input PDF Page Height
  46. RESIZE_WIDTH="" # Resized PDF Page Width
  47. RESIZE_HEIGHT="" # Resized PDF Page Height
  48. IMAGE_RESOLUTION=300 # Image resolution (dpi) (300 is /Printer default)
  49. ########################## EXIT FLAGS ##########################
  50. EXIT_SUCCESS=0
  51. EXIT_ERROR=1
  52. EXIT_INVALID_PAGE_SIZE_DETECTED=10
  53. EXIT_FILE_NOT_FOUND=20
  54. EXIT_INPUT_NOT_PDF=21
  55. EXIT_INVALID_OPTION=22
  56. EXIT_NO_INPUT_FILE=23
  57. EXIT_INVALID_SCALE=24
  58. EXIT_MISSING_DEPENDENCY=25
  59. EXIT_IMAGEMAGIK_NOT_FOUND=26
  60. EXIT_MAC_MDLS_NOT_FOUND=27
  61. EXIT_PDFINFO_NOT_FOUND=28
  62. EXIT_NOWRITE_PERMISSION=29
  63. EXIT_NOREAD_PERMISSION=30
  64. EXIT_TEMP_FILE_EXISTS=40
  65. EXIT_INVALID_PAPER_SIZE=50
  66. EXIT_INVALID_IMAGE_RESOLUTION=51
  67. ############################# MAIN #############################
  68. # Main function called at the end
  69. main() {
  70. printPDFSizes # may exit here
  71. local finalRet=$EXIT_ERROR
  72. if isMixedMode; then
  73. initMain " Mixed Tasks: Resize & Scale"
  74. local tempFile=""
  75. local tempSuffix="$RANDOM$RANDOM""_TEMP_$RANDOM$RANDOM.pdf"
  76. outputFile="$OUTFILEPDF" # backup outFile name
  77. tempFile="${OUTFILEPDF%.pdf}.$tempSuffix" # set a temp file name
  78. if isFile "$tempFile"; then
  79. printError $'Error! Temporary file name already exists!\n'"File: $tempFile"$'\nAborting execution to avoid overwriting the file.\nPlease Try again...'
  80. exit $EXIT_TEMP_FILE_EXISTS
  81. fi
  82. OUTFILEPDF="$tempFile" # set output to tmp file
  83. pageResize # resize to tmp file
  84. finalRet=$?
  85. INFILEPDF="$tempFile" # get tmp file as input
  86. OUTFILEPDF="$outputFile" # reset final target
  87. PGWIDTH=$RESIZE_WIDTH # we already know the new page size
  88. PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize)
  89. vPrintPageSizes ' New'
  90. vPrintScaleFactor
  91. pageScale # scale the resized pdf
  92. finalRet=$(($finalRet+$?))
  93. # remove tmp file
  94. isFile "$tempFile" && rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile"
  95. elif isResizeMode; then
  96. initMain " Single Task: Resize PDF Paper"
  97. vPrintScaleFactor "Disabled (resize only)"
  98. pageResize
  99. finalRet=$?
  100. else
  101. initMain " Single Task: Scale PDF Contents"
  102. local scaleMode=""
  103. isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)'
  104. vPrintScaleFactor "$SCALE $scaleMode"
  105. pageScale
  106. finalRet=$?
  107. fi
  108. if [[ $finalRet -eq $EXIT_SUCCESS ]] && isEmpty "$GS_RUN_STATUS"; then
  109. vprint " Final Status: File created successfully"
  110. else
  111. vprint " Final Status: Error detected. Exit status: $finalRet"
  112. printError "PdfScale: ERROR!"$'\n'"Ghostscript Debug Info:"$'\n'"$GS_RUN_STATUS"
  113. fi
  114. return $finalRet
  115. }
  116. # Initializes PDF processing for all modes of operation
  117. initMain() {
  118. printVersion 1 'verbose'
  119. isNotEmpty "$1" && vprint "$1"
  120. vPrintFileInfo
  121. getPageSize
  122. vPrintPageSizes ' Source'
  123. }
  124. # Prints PDF Info and exits with $EXIT_SUCCESS, but only if $JUST_IDENTIFY is $TRUE
  125. printPDFSizes() {
  126. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  127. VERBOSE=0
  128. printVersion 3 " - Paper Sizes"
  129. getPageSize || initError "Could not get pagesize!"
  130. local paperType="$(getGSPaperName $PGWIDTH $PGHEIGHT)"
  131. isEmpty "$paperType" && paperType="Custom Paper Size"
  132. printf '%s\n' "------------+-----------------------------"
  133. printf " File | %s\n" "$(basename "$INFILEPDF")"
  134. printf " Paper Type | %s\n" "$paperType"
  135. printf '%s\n' "------------+-----------------------------"
  136. printf '%s\n' " | WIDTH x HEIGHT"
  137. printf " Points | %+8s x %-8s\n" "$PGWIDTH" "$PGHEIGHT"
  138. printf " Milimeters | %+8s x %-8s\n" "$(pointsToMilimeters $PGWIDTH)" "$(pointsToMilimeters $PGHEIGHT)"
  139. printf " Inches | %+8s x %-8s\n" "$(pointsToInches $PGWIDTH)" "$(pointsToInches $PGHEIGHT)"
  140. exit $EXIT_SUCCESS
  141. fi
  142. return $EXIT_SUCCESS
  143. }
  144. ###################### GHOSTSCRIPT CALLS #######################
  145. # Runs the ghostscript scaling script
  146. pageScale() {
  147. # Compute translation factors (to center page).
  148. XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  149. YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  150. vprint " Translation X: $XTRANS"
  151. vprint " Translation Y: $YTRANS"
  152. local increase=$(echo "scale=0; (($SCALE - 1) * 100)/1" | "$BCBIN")
  153. vprint " Run Scaling: $increase %"
  154. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageScale 2>&1)"
  155. return $? # Last command is always returned I think
  156. }
  157. # Runs GS call for scaling, nothing else should run here
  158. gsPageScale() {
  159. # Scale page
  160. "$GSBIN" \
  161. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  162. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  163. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  164. -dColorConversionStrategy=/LeaveColorUnchanged \
  165. -dSubsetFonts=true -dEmbedAllFonts=true \
  166. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  167. -sOutputFile="$OUTFILEPDF" \
  168. -c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  169. -f "$INFILEPDF"
  170. return $?
  171. }
  172. # Runs the ghostscript paper resize script
  173. pageResize() {
  174. # Get paper sizes from source if not resizing
  175. isResizePaperSource && { RESIZE_WIDTH=$PGWIDTH; RESIZE_HEIGHT=$PGHEIGHT; }
  176. # Get new paper sizes if not custom or source paper
  177. isNotCustomPaper && ! isResizePaperSource && getGSPaperSize "$RESIZE_PAPER_TYPE"
  178. vprint " Auto Rotate: $(basename $AUTO_ROTATION)"
  179. runFlipDetect
  180. vprint " Run Resizing: $(uppercase "$RESIZE_PAPER_TYPE") ( "$RESIZE_WIDTH" x "$RESIZE_HEIGHT" ) pts"
  181. GS_RUN_STATUS="$GS_RUN_STATUS""$(gsPageResize 2>&1)"
  182. return $?
  183. }
  184. # Runs GS call for resizing, nothing else should run here
  185. gsPageResize() {
  186. # Change page size
  187. "$GSBIN" \
  188. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  189. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  190. -dColorImageResolution=$IMAGE_RESOLUTION -dGrayImageResolution=$IMAGE_RESOLUTION \
  191. -dColorConversionStrategy=/LeaveColorUnchanged \
  192. -dSubsetFonts=true -dEmbedAllFonts=true \
  193. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  194. -dAutoRotatePages=$AUTO_ROTATION \
  195. -dFIXEDMEDIA -dPDFFitPage \
  196. -sOutputFile="$OUTFILEPDF" \
  197. -f "$INFILEPDF"
  198. return $?
  199. }
  200. # Returns $TRUE if we should use the source paper size, $FALSE otherwise
  201. isResizePaperSource() {
  202. [[ "$RESIZE_PAPER_TYPE" = 'source' ]] && return $TRUE
  203. return $FALSE
  204. }
  205. # Filp-Detect Logic
  206. runFlipDetect() {
  207. if isFlipForced; then
  208. vprint " Flip Detect: Forced Mode!"
  209. applyFlipRevert
  210. elif isFlipDetectionEnabled && shouldFlip; then
  211. vprint " Flip Detect: Wrong orientation detected!"
  212. applyFlipRevert
  213. elif ! isFlipDetectionEnabled; then
  214. vprint " Flip Detect: Disabled"
  215. else
  216. vprint " Flip Detect: No change needed"
  217. fi
  218. }
  219. # Inverts $RESIZE_HEIGHT with $RESIZE_WIDTH
  220. applyFlipRevert() {
  221. local tmpInverter=""
  222. tmpInverter=$RESIZE_HEIGHT
  223. RESIZE_HEIGHT=$RESIZE_WIDTH
  224. RESIZE_WIDTH=$tmpInverter
  225. vprint " Inverting Width <-> Height"
  226. }
  227. # Returns the $FLIP_DETECTION flag
  228. isFlipDetectionEnabled() {
  229. return $FLIP_DETECTION
  230. }
  231. # Returns the $FLIP_FORCE flag
  232. isFlipForced() {
  233. return $FLIP_FORCE
  234. }
  235. # Returns $TRUE if the the paper size will invert orientation from source, $FALSE otherwise
  236. shouldFlip() {
  237. [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]] || [[ $PGWIDTH -lt $PGHEIGHT && $RESIZE_WIDTH -gt $RESIZE_HEIGHT ]] && return $TRUE
  238. return $FALSE
  239. }
  240. ########################## INITIALIZERS #########################
  241. # Loads external dependencies and checks for errors
  242. initDeps() {
  243. GREPBIN="$(which grep 2>/dev/null)"
  244. GSBIN="$(which gs 2>/dev/null)"
  245. BCBIN="$(which bc 2>/dev/null)"
  246. IDBIN=$(which identify 2>/dev/null)
  247. MDLSBIN="$(which mdls 2>/dev/null)"
  248. PDFINFOBIN="$(which pdfinfo 2>/dev/null)"
  249. vprint "Checking for basename, grep, ghostscript and bcmath"
  250. basename "" >/dev/null 2>&1 || printDependency 'basename'
  251. isNotAvailable "$GREPBIN" && printDependency 'grep'
  252. isNotAvailable "$GSBIN" && printDependency 'ghostscript'
  253. isNotAvailable "$BCBIN" && printDependency 'bc'
  254. return $TRUE
  255. }
  256. # Checks for dependencies errors, run after getting options
  257. checkDeps() {
  258. if [[ $MODE = "IDENTIFY" ]]; then
  259. vprint "Checking for imagemagick's identify"
  260. if isNotAvailable "$IDBIN"; then printDependency 'imagemagick'; fi
  261. fi
  262. if [[ $MODE = "PDFINFO" ]]; then
  263. vprint "Checking for pdfinfo"
  264. if isNotAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi
  265. fi
  266. if [[ $MODE = "MDLS" ]]; then
  267. vprint "Checking for MacOS mdls"
  268. if isNotAvailable "$MDLSBIN"; then
  269. initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND
  270. fi
  271. fi
  272. return $TRUE
  273. }
  274. ######################### CLI OPTIONS ##########################
  275. # Parse options
  276. getOptions() {
  277. local _optArgs=() # things that do not start with a '-'
  278. local _tgtFile="" # to set $OUTFILEPDF
  279. local _currParam="" # to enable case-insensitiveness
  280. while [ ${#} -gt 0 ]; do
  281. if [[ "${1:0:2}" = '--' ]]; then
  282. # Long Option, get lowercase version
  283. _currParam="$(lowercase ${1})"
  284. elif [[ "${1:0:1}" = '-' ]]; then
  285. # short Option, just assign
  286. _currParam="${1}"
  287. else
  288. # file name arguments, store as is and reset loop
  289. _optArgs+=("$1")
  290. shift
  291. continue
  292. fi
  293. case "$_currParam" in
  294. -v|--verbose)
  295. ((VERBOSE++))
  296. shift
  297. ;;
  298. -n|--no-overwrite|--nooverwrite)
  299. ABORT_ON_OVERWRITE=$TRUE
  300. shift
  301. ;;
  302. -h|--help)
  303. printHelp
  304. exit $EXIT_SUCCESS
  305. ;;
  306. -V|--version)
  307. printVersion 3
  308. exit $EXIT_SUCCESS
  309. ;;
  310. -i|--identify|--info)
  311. JUST_IDENTIFY=$TRUE
  312. shift
  313. ;;
  314. -s|--scale|--setscale|--set-scale)
  315. shift
  316. parseScale "$1"
  317. shift
  318. ;;
  319. -m|--mode|--paperdetect|--paper-detect|--pagesizemode|--page-size-mode)
  320. shift
  321. parseMode "$1"
  322. shift
  323. ;;
  324. -r|--resize)
  325. shift
  326. parsePaperResize "$1"
  327. shift
  328. ;;
  329. -p|--printpapers|--print-papers|--listpapers|--list-papers)
  330. printPaperInfo
  331. exit $EXIT_SUCCESS
  332. ;;
  333. -f|--flipdetection|--flip-detection|--flip-mode|--flipmode|--flipdetect|--flip-detect)
  334. shift
  335. parseFlipDetectionMode "$1"
  336. shift
  337. ;;
  338. -a|--autorotation|--auto-rotation|--autorotate|--auto-rotate)
  339. shift
  340. parseAutoRotationMode "$1"
  341. shift
  342. ;;
  343. --image-resolution)
  344. shift
  345. parseImageResolution "$1"
  346. shift
  347. ;;
  348. *)
  349. initError "Invalid Parameter: \"$1\"" $EXIT_INVALID_OPTION
  350. ;;
  351. esac
  352. done
  353. isEmpty "${_optArgs[2]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[2]}" $EXIT_INVALID_OPTION
  354. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  355. isEmpty "${_optArgs[1]}" || initError "Seems like you passed an extra file name?"$'\n'"Invalid option: ${_optArgs[1]}" $EXIT_INVALID_OPTION
  356. VERBOSE=0 # remove verboseness if present
  357. fi
  358. # Validate input PDF file
  359. INFILEPDF="${_optArgs[0]}"
  360. isEmpty "$INFILEPDF" && initError "Input file is empty!" $EXIT_NO_INPUT_FILE
  361. isPDF "$INFILEPDF" || initError "Input file is not a PDF file: $INFILEPDF" $EXIT_INPUT_NOT_PDF
  362. isFile "$INFILEPDF" || initError "Input file not found: $INFILEPDF" $EXIT_FILE_NOT_FOUND
  363. isReadable "$INFILEPDF" || initError "No read access to input file: $INFILEPDF"$'\nPermission Denied' $EXIT_NOREAD_PERMISSION
  364. checkDeps
  365. if [[ $JUST_IDENTIFY -eq $TRUE ]]; then
  366. return $TRUE # no need to get output file, so return already
  367. fi
  368. _tgtFile="${_optArgs[1]}"
  369. local _autoName="${INFILEPDF%.*}" # remove possible stupid extension, like .pDF
  370. if isMixedMode; then
  371. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf"
  372. elif isResizeMode; then
  373. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.$(uppercase $RESIZE_PAPER_TYPE).pdf"
  374. else
  375. isEmpty "$_tgtFile" && OUTFILEPDF="${_autoName}.SCALED.pdf"
  376. fi
  377. isNotEmpty "$_tgtFile" && OUTFILEPDF="${_tgtFile%.pdf}.pdf"
  378. validateOutFile
  379. }
  380. # Checks if output file is valid and writable
  381. validateOutFile() {
  382. local _tgtDir="$(dirname "$OUTFILEPDF")"
  383. isDir "$_tgtDir" || initError "Output directory does not exist!"$'\n'"Target Dir: $_tgtDir" $EXIT_NOWRITE_PERMISSION
  384. 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
  385. isTouchable "$OUTFILEPDF" || initError "Could not get write permission for output file!"$'\n'"Target File: $OUTFILEPDF"$'\nPermission Denied' $EXIT_NOWRITE_PERMISSION
  386. }
  387. # Returns $TRUE if we should not overwrite $OUTFILEPDF, $FALSE otherwise
  388. isAbortOnOverwrite() {
  389. return $ABORT_ON_OVERWRITE
  390. }
  391. # Parses and validates the scaling factor
  392. parseScale() {
  393. AUTOMATIC_SCALING=$FALSE
  394. if ! isFloatBiggerThanZero "$1"; then
  395. printError "Invalid factor: $1"
  396. printError "The factor must be a floating point number greater than 0"
  397. printError "Example: for 80% use 0.8"
  398. exit $EXIT_INVALID_SCALE
  399. fi
  400. SCALE="$1"
  401. }
  402. # Parse a forced mode of operation
  403. parseMode() {
  404. local param="$(lowercase $1)"
  405. case "${param}" in
  406. c|catgrep|'cat+grep'|grep|g)
  407. ADAPTIVEMODE=$FALSE
  408. MODE="CATGREP"
  409. return $TRUE
  410. ;;
  411. i|imagemagick|identify)
  412. ADAPTIVEMODE=$FALSE
  413. MODE="IDENTIFY"
  414. return $TRUE
  415. ;;
  416. m|mdls|quartz|mac)
  417. ADAPTIVEMODE=$FALSE
  418. MODE="MDLS"
  419. return $TRUE
  420. ;;
  421. p|pdfinfo)
  422. ADAPTIVEMODE=$FALSE
  423. MODE="PDFINFO"
  424. return $TRUE
  425. ;;
  426. a|auto|automatic|adaptive)
  427. ADAPTIVEMODE=$TRUE
  428. MODE=""
  429. return $TRUE
  430. ;;
  431. *)
  432. initError "Invalid PDF Size Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  433. return $FALSE
  434. ;;
  435. esac
  436. return $FALSE
  437. }
  438. # Parses and validates the scaling factor
  439. parseFlipDetectionMode() {
  440. local param="$(lowercase $1)"
  441. case "${param}" in
  442. d|disable)
  443. FLIP_DETECTION=$FALSE
  444. FLIP_FORCE=$FALSE
  445. ;;
  446. f|force)
  447. FLIP_DETECTION=$FALSE
  448. FLIP_FORCE=$TRUE
  449. ;;
  450. a|auto|automatic)
  451. FLIP_DETECTION=$TRUE
  452. FLIP_FORCE=$FALSE
  453. ;;
  454. *)
  455. initError "Invalid Flip Detection Mode: \"$1\"" $EXIT_INVALID_OPTION
  456. return $FALSE
  457. ;;
  458. esac
  459. }
  460. # Parses and validates the scaling factor
  461. parseAutoRotationMode() {
  462. local param="$(lowercase $1)"
  463. case "${param}" in
  464. n|none)
  465. AUTO_ROTATION='/None'
  466. ;;
  467. a|all)
  468. AUTO_ROTATION='/All'
  469. ;;
  470. p|pagebypage|auto)
  471. AUTO_ROTATION='/PageByPage'
  472. ;;
  473. *)
  474. initError "Invalid Auto Rotation Mode: \"$1\"" $EXIT_INVALID_OPTION
  475. return $FALSE
  476. ;;
  477. esac
  478. }
  479. # Validades the a paper resize CLI option and sets the paper to $RESIZE_PAPER_TYPE
  480. parsePaperResize() {
  481. isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE
  482. local lowercasePaper="$(lowercase $1)"
  483. local customPaper=($lowercasePaper)
  484. if [[ "$customPaper" = 'same' || "$customPaper" = 'keep' || "$customPaper" = 'source' ]]; then
  485. RESIZE_PAPER_TYPE='source'
  486. elif [[ "${customPaper[0]}" = 'custom' ]]; then
  487. if isNotValidMeasure "${customPaper[1]}" || ! isFloatBiggerThanZero "${customPaper[2]}" || ! isFloatBiggerThanZero "${customPaper[3]}"; then
  488. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  489. fi
  490. RESIZE_PAPER_TYPE="custom"
  491. CUSTOM_RESIZE_PAPER=$TRUE
  492. if isMilimeter "${customPaper[1]}"; then
  493. RESIZE_WIDTH="$(milimetersToPoints "${customPaper[2]}")"
  494. RESIZE_HEIGHT="$(milimetersToPoints "${customPaper[3]}")"
  495. elif isInch "${customPaper[1]}"; then
  496. RESIZE_WIDTH="$(inchesToPoints "${customPaper[2]}")"
  497. RESIZE_HEIGHT="$(inchesToPoints "${customPaper[3]}")"
  498. elif isPoint "${customPaper[1]}"; then
  499. RESIZE_WIDTH="${customPaper[2]}"
  500. RESIZE_HEIGHT="${customPaper[3]}"
  501. else
  502. initError "Invalid Custom Paper Definition!"$'\n'"Use: -r 'custom <measurement> <width> <height>'"$'\n'"Measurements: mm, in, pts" $EXIT_INVALID_OPTION
  503. fi
  504. else
  505. isPaperName "$lowercasePaper" || initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE
  506. RESIZE_PAPER_TYPE="$lowercasePaper"
  507. fi
  508. }
  509. # Parses and validates the scaling factor
  510. parseImageResolution() {
  511. if isNotAnInteger "$1"; then
  512. printError "Invalid image resolution: $1"
  513. printError "The image resolution must be an integer"
  514. exit $EXIT_INVALID_IMAGE_RESOLUTION
  515. fi
  516. IMAGE_RESOLUTION="$1"
  517. }
  518. ################### PDF PAGE SIZE DETECTION ####################
  519. ################################################################
  520. # Detects operation mode and also runs the adaptive mode
  521. # PAGESIZE LOGIC
  522. # 1- Try to get Mediabox with GREP
  523. # 2- MacOS => try to use mdls
  524. # 3- Try to use pdfinfo
  525. # 4- Try to use identify (imagemagick)
  526. # 5- Fail
  527. ################################################################
  528. getPageSize() {
  529. if isNotAdaptiveMode; then
  530. vprint " Get Page Size: Adaptive Disabled"
  531. if [[ $MODE = "CATGREP" ]]; then
  532. vprint " Method: Grep"
  533. getPageSizeCatGrep
  534. elif [[ $MODE = "MDLS" ]]; then
  535. vprint " Method: Mac Quartz mdls"
  536. getPageSizeMdls
  537. elif [[ $MODE = "PDFINFO" ]]; then
  538. vprint " Method: PDFInfo"
  539. getPageSizePdfInfo
  540. elif [[ $MODE = "IDENTIFY" ]]; then
  541. vprint " Method: ImageMagick's Identify"
  542. getPageSizeImagemagick
  543. else
  544. printError "Error! Invalid Mode: $MODE"
  545. printError "Aborting execution..."
  546. exit $EXIT_INVALID_OPTION
  547. fi
  548. return $TRUE
  549. fi
  550. vprint " Get Page Size: Adaptive Enabled"
  551. vprint " Method: Grep"
  552. getPageSizeCatGrep
  553. if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then
  554. vprint " Failed"
  555. vprint " Method: Mac Quartz mdls"
  556. getPageSizeMdls
  557. fi
  558. if pageSizeIsInvalid; then
  559. vprint " Failed"
  560. vprint " Method: PDFInfo"
  561. getPageSizePdfInfo
  562. fi
  563. if pageSizeIsInvalid; then
  564. vprint " Failed"
  565. vprint " Method: ImageMagick's Identify"
  566. getPageSizeImagemagick
  567. fi
  568. if pageSizeIsInvalid; then
  569. vprint " Failed"
  570. printError "Error when detecting PDF paper size!"
  571. printError "All methods of detection failed"
  572. printError "You may want to install pdfinfo or imagemagick"
  573. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  574. fi
  575. return $TRUE
  576. }
  577. # Gets page size using imagemagick's identify
  578. getPageSizeImagemagick() {
  579. # Sanity and Adaptive together
  580. if isNotFile "$IDBIN" && isNotAdaptiveMode; then
  581. notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify"
  582. elif isNotFile "$IDBIN" && isAdaptiveMode; then
  583. return $FALSE
  584. fi
  585. # get data from image magick
  586. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  587. if isEmpty "$identify" && isNotAdaptiveMode; then
  588. notAdaptiveFailed "ImageMagicks's Identify returned an empty string!"
  589. elif isEmpty "$identify" && isAdaptiveMode; then
  590. return $FALSE
  591. fi
  592. identify="${identify%%BREAKME*}" # get page size only for 1st page
  593. identify=($identify) # make it an array
  594. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  595. PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign
  596. return $TRUE
  597. }
  598. # Gets page size using Mac Quarts mdls
  599. getPageSizeMdls() {
  600. # Sanity and Adaptive together
  601. if isNotFile "$MDLSBIN" && isNotAdaptiveMode; then
  602. notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls"
  603. elif isNotFile "$MDLSBIN" && isAdaptiveMode; then
  604. return $FALSE
  605. fi
  606. local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)"
  607. if isEmpty "$identify" && isNotAdaptiveMode; then
  608. notAdaptiveFailed "Mac Quartz mdls returned an empty string!"
  609. elif isEmpty "$identify" && isAdaptiveMode; then
  610. return $FALSE
  611. fi
  612. identify=${identify//$'\t'/ } # change tab to space
  613. identify=($identify) # make it an array
  614. if [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isNotAdaptiveMode; then
  615. notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?"
  616. elif [[ "${identify[5]}" = "(null)" || "${identify[2]}" = "(null)" ]] && isAdaptiveMode; then
  617. return $FALSE
  618. fi
  619. PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign
  620. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  621. return $TRUE
  622. }
  623. # Gets page size using Linux PdfInfo
  624. getPageSizePdfInfo() {
  625. # Sanity and Adaptive together
  626. if isNotFile "$PDFINFOBIN" && isNotAdaptiveMode; then
  627. notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo"
  628. elif isNotFile "$PDFINFOBIN" && isAdaptiveMode; then
  629. return $FALSE
  630. fi
  631. # get data from image magick
  632. local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | "$GREPBIN" -i 'Page size:' )"
  633. if isEmpty "$identify" && isNotAdaptiveMode; then
  634. notAdaptiveFailed "Linux PdfInfo returned an empty string!"
  635. elif isEmpty "$identify" && isAdaptiveMode; then
  636. return $FALSE
  637. fi
  638. identify="${identify##*Page size:}" # remove stuff
  639. identify=($identify) # make it an array
  640. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  641. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  642. return $TRUE
  643. }
  644. # Gets page size using cat and grep
  645. getPageSizeCatGrep() {
  646. # get MediaBox info from PDF file using grep, these are all possible
  647. # /MediaBox [0 0 595 841]
  648. # /MediaBox [ 0 0 595.28 841.89]
  649. # /MediaBox[ 0 0 595.28 841.89 ]
  650. # Get MediaBox data if possible
  651. local mediaBox="$("$GREPBIN" -a -e '/MediaBox' -m 1 "$INFILEPDF" 2>/dev/null)"
  652. mediaBox="${mediaBox##*/MediaBox}"
  653. # No page size data available
  654. if isEmpty "$mediaBox" && isNotAdaptiveMode; then
  655. notAdaptiveFailed "There is no MediaBox in the pdf document!"
  656. elif isEmpty "$mediaBox" && isAdaptiveMode; then
  657. return $FALSE
  658. fi
  659. # remove chars [ and ]
  660. mediaBox="${mediaBox//[}"
  661. mediaBox="${mediaBox//]}"
  662. mediaBox=($mediaBox) # make it an array
  663. mbCount=${#mediaBox[@]} # array size
  664. # sanity
  665. if [[ $mbCount -lt 4 ]]; then
  666. printError "Error when reading the page size!"
  667. printError "The page size information is invalid!"
  668. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  669. fi
  670. # we are done
  671. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  672. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  673. return $TRUE
  674. }
  675. # Prints error message and exits execution
  676. notAdaptiveFailed() {
  677. local errProgram="$2"
  678. local errStr="$1"
  679. if isEmpty "$2"; then
  680. printError "Error when reading input file!"
  681. printError "Could not determine the page size!"
  682. else
  683. printError "Error! $2 was not found!"
  684. fi
  685. isNotEmpty "$errStr" && printError "$errStr"
  686. printError "Aborting! You may want to try the adaptive mode."
  687. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  688. }
  689. # Verbose print of the Width and Height (Source or New) to screen
  690. vPrintPageSizes() {
  691. vprint " $1 Width: $PGWIDTH postscript-points"
  692. vprint "$1 Height: $PGHEIGHT postscript-points"
  693. }
  694. #################### GHOSTSCRIPT PAPER INFO ####################
  695. # Loads valid paper info to memory
  696. getPaperInfo() {
  697. # name inchesW inchesH mmW mmH pointsW pointsH
  698. sizesUS="\
  699. 11x17 11.0 17.0 279 432 792 1224
  700. ledger 17.0 11.0 432 279 1224 792
  701. legal 8.5 14.0 216 356 612 1008
  702. letter 8.5 11.0 216 279 612 792
  703. lettersmall 8.5 11.0 216 279 612 792
  704. archE 36.0 48.0 914 1219 2592 3456
  705. archD 24.0 36.0 610 914 1728 2592
  706. archC 18.0 24.0 457 610 1296 1728
  707. archB 12.0 18.0 305 457 864 1296
  708. archA 9.0 12.0 229 305 648 864"
  709. sizesISO="\
  710. a0 33.1 46.8 841 1189 2384 3370
  711. a1 23.4 33.1 594 841 1684 2384
  712. a2 16.5 23.4 420 594 1191 1684
  713. a3 11.7 16.5 297 420 842 1191
  714. a4 8.3 11.7 210 297 595 842
  715. a4small 8.3 11.7 210 297 595 842
  716. a5 5.8 8.3 148 210 420 595
  717. a6 4.1 5.8 105 148 297 420
  718. a7 2.9 4.1 74 105 210 297
  719. a8 2.1 2.9 52 74 148 210
  720. a9 1.5 2.1 37 52 105 148
  721. a10 1.0 1.5 26 37 73 105
  722. isob0 39.4 55.7 1000 1414 2835 4008
  723. isob1 27.8 39.4 707 1000 2004 2835
  724. isob2 19.7 27.8 500 707 1417 2004
  725. isob3 13.9 19.7 353 500 1001 1417
  726. isob4 9.8 13.9 250 353 709 1001
  727. isob5 6.9 9.8 176 250 499 709
  728. isob6 4.9 6.9 125 176 354 499
  729. c0 36.1 51.1 917 1297 2599 3677
  730. c1 25.5 36.1 648 917 1837 2599
  731. c2 18.0 25.5 458 648 1298 1837
  732. c3 12.8 18.0 324 458 918 1298
  733. c4 9.0 12.8 229 324 649 918
  734. c5 6.4 9.0 162 229 459 649
  735. c6 4.5 6.4 114 162 323 459"
  736. sizesJIS="\
  737. jisb0 NA NA 1030 1456 2920 4127
  738. jisb1 NA NA 728 1030 2064 2920
  739. jisb2 NA NA 515 728 1460 2064
  740. jisb3 NA NA 364 515 1032 1460
  741. jisb4 NA NA 257 364 729 1032
  742. jisb5 NA NA 182 257 516 729
  743. jisb6 NA NA 128 182 363 516"
  744. sizesOther="\
  745. flsa 8.5 13.0 216 330 612 936
  746. flse 8.5 13.0 216 330 612 936
  747. halfletter 5.5 8.5 140 216 396 612
  748. hagaki 3.9 5.8 100 148 283 420"
  749. sizesAll="\
  750. $sizesUS
  751. $sizesISO
  752. $sizesJIS
  753. $sizesOther"
  754. }
  755. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  756. getGSPaperSize() {
  757. isEmpty "$sizesall" && getPaperInfo
  758. while read l; do
  759. local cols=($l)
  760. if [[ "$1" == ${cols[0]} ]]; then
  761. RESIZE_WIDTH=${cols[5]}
  762. RESIZE_HEIGHT=${cols[6]}
  763. return $TRUE
  764. fi
  765. done <<< "$sizesAll"
  766. }
  767. # Gets a paper size in points and sets it to RESIZE_WIDTH and RESIZE_HEIGHT
  768. getGSPaperName() {
  769. local w="$(printf "%.0f" $1)"
  770. local h="$(printf "%.0f" $2)"
  771. isEmpty "$sizesall" && getPaperInfo
  772. # Because US Standard has inverted sizes, I need to scan 2 times
  773. # instead of just testing if width is bigger than height
  774. while read l; do
  775. local cols=($l)
  776. if [[ "$w" == ${cols[5]} && "$h" == ${cols[6]} ]]; then
  777. printf "%s Portrait" $(uppercase ${cols[0]})
  778. return $TRUE
  779. fi
  780. done <<< "$sizesAll"
  781. while read l; do
  782. local cols=($l)
  783. if [[ "$w" == ${cols[6]} && "$h" == ${cols[5]} ]]; then
  784. printf "%s Landscape" $(uppercase ${cols[0]})
  785. return $TRUE
  786. fi
  787. done <<< "$sizesAll"
  788. return $FALSE
  789. }
  790. # Loads an array with paper names to memory
  791. getPaperNames() {
  792. 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 \
  793. 11x17 ledger legal letter lettersmall archE archD archC archB archA \
  794. jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \
  795. flsa flse halfletter hagaki)
  796. }
  797. # Prints uppercase paper names to screen (used in help)
  798. printPaperNames() {
  799. isEmpty "$paperNames" && getPaperNames
  800. for i in "${!paperNames[@]}"; do
  801. [[ $i -eq 0 ]] && echo -n -e ' '
  802. [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo -n -e $'\n '
  803. ppN="$(uppercase ${paperNames[i]})"
  804. printf "%-14s" "$ppN"
  805. done
  806. echo ""
  807. }
  808. # Returns $TRUE if $! is a valid paper name, $FALSE otherwise
  809. isPaperName() {
  810. isEmpty "$1" && return $FALSE
  811. isEmpty "$paperNames" && getPaperNames
  812. for i in "${paperNames[@]}"; do
  813. [[ "$i" = "$1" ]] && return $TRUE
  814. done
  815. return $FALSE
  816. }
  817. # Prints all tables with ghostscript paper information
  818. printPaperInfo() {
  819. printVersion 3
  820. echo $'\n'"Paper Sizes Information"$'\n'
  821. getPaperInfo
  822. printPaperTable "ISO STANDARD" "$sizesISO"; echo
  823. printPaperTable "US STANDARD" "$sizesUS"; echo
  824. printPaperTable "JIS STANDARD *Aproximated Points" "$sizesJIS"; echo
  825. printPaperTable "OTHERS" "$sizesOther"; echo
  826. }
  827. # GS paper table helper, prints a full line
  828. printTableLine() {
  829. echo '+-----------------------------------------------------------------+'
  830. }
  831. # GS paper table helper, prints a line with dividers
  832. printTableDivider() {
  833. echo '+-----------------+-------+-------+-------+-------+-------+-------+'
  834. }
  835. # GS paper table helper, prints a table header
  836. printTableHeader() {
  837. echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |'
  838. }
  839. # GS paper table helper, prints a table title
  840. printTableTitle() {
  841. printf "| %-64s%s\n" "$1" '|'
  842. }
  843. # GS paper table printer, prints a table for a paper variable
  844. printPaperTable() {
  845. printTableLine
  846. printTableTitle "$1"
  847. printTableLine
  848. printTableHeader
  849. printTableDivider
  850. while read l; do
  851. local cols=($l)
  852. printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]};
  853. done <<< "$2"
  854. printTableDivider
  855. }
  856. # Returns $TRUE if $1 is a valid measurement for a custom paper, $FALSE otherwise
  857. isNotValidMeasure() {
  858. isMilimeter "$1" || isInch "$1" || isPoint "$1" && return $FALSE
  859. return $TRUE
  860. }
  861. # Returns $TRUE if $1 is a valid milimeter string, $FALSE otherwise
  862. isMilimeter() {
  863. [[ "$1" = 'mm' || "$1" = 'milimeters' || "$1" = 'milimeter' ]] && return $TRUE
  864. return $FALSE
  865. }
  866. # Returns $TRUE if $1 is a valid inch string, $FALSE otherwise
  867. isInch() {
  868. [[ "$1" = 'in' || "$1" = 'inch' || "$1" = 'inches' ]] && return $TRUE
  869. return $FALSE
  870. }
  871. # Returns $TRUE if $1 is a valid point string, $FALSE otherwise
  872. isPoint() {
  873. [[ "$1" = 'pt' || "$1" = 'pts' || "$1" = 'point' || "$1" = 'points' ]] && return $TRUE
  874. return $FALSE
  875. }
  876. # Returns $TRUE if a custom paper is being used, $FALSE otherwise
  877. isCustomPaper() {
  878. return $CUSTOM_RESIZE_PAPER
  879. }
  880. # Returns $FALSE if a custom paper is being used, $TRUE otherwise
  881. isNotCustomPaper() {
  882. isCustomPaper && return $FALSE
  883. return $TRUE
  884. }
  885. ######################### CONVERSIONS ##########################
  886. # Prints the lowercase char value for $1
  887. lowercaseChar() {
  888. case "$1" in
  889. [A-Z])
  890. n=$(printf "%d" "'$1")
  891. n=$((n+32))
  892. printf \\$(printf "%o" "$n")
  893. ;;
  894. *)
  895. printf "%s" "$1"
  896. ;;
  897. esac
  898. }
  899. # Prints the lowercase version of a string
  900. lowercase() {
  901. word="$@"
  902. for((i=0;i<${#word};i++))
  903. do
  904. ch="${word:$i:1}"
  905. lowercaseChar "$ch"
  906. done
  907. }
  908. # Prints the uppercase char value for $1
  909. uppercaseChar(){
  910. case "$1" in
  911. [a-z])
  912. n=$(printf "%d" "'$1")
  913. n=$((n-32))
  914. printf \\$(printf "%o" "$n")
  915. ;;
  916. *)
  917. printf "%s" "$1"
  918. ;;
  919. esac
  920. }
  921. # Prints the uppercase version of a string
  922. uppercase() {
  923. word="$@"
  924. for((i=0;i<${#word};i++))
  925. do
  926. ch="${word:$i:1}"
  927. uppercaseChar "$ch"
  928. done
  929. }
  930. # Prints the postscript points rounded equivalent from $1 mm
  931. milimetersToPoints() {
  932. local pts=$(echo "scale=8; $1 * 72 / 25.4" | "$BCBIN")
  933. printf '%.0f' "$pts" # Print rounded conversion
  934. }
  935. # Prints the postscript points rounded equivalent from $1 inches
  936. inchesToPoints() {
  937. local pts=$(echo "scale=8; $1 * 72" | "$BCBIN")
  938. printf '%.0f' "$pts" # Print rounded conversion
  939. }
  940. # Prints the mm equivalent from $1 postscript points
  941. pointsToMilimeters() {
  942. local pts=$(echo "scale=8; $1 / 72 * 25.4" | "$BCBIN")
  943. printf '%.0f' "$pts" # Print rounded conversion
  944. }
  945. # Prints the inches equivalent from $1 postscript points
  946. pointsToInches() {
  947. local pts=$(echo "scale=8; $1 / 72" | "$BCBIN")
  948. printf '%.1f' "$pts" # Print rounded conversion
  949. }
  950. ######################## MODE-DETECTION ########################
  951. # Returns $TRUE if the scale was set manually, $FALSE if we are using automatic scaling
  952. isManualScaledMode() {
  953. [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE
  954. return $TRUE
  955. }
  956. # Returns true if we are resizing a paper (ignores scaling), false otherwise
  957. isResizeMode() {
  958. isEmpty $RESIZE_PAPER_TYPE && return $FALSE
  959. return $TRUE
  960. }
  961. # Returns true if we are resizing a paper and the scale was manually set
  962. isMixedMode() {
  963. isResizeMode && isManualScaledMode && return $TRUE
  964. return $FALSE
  965. }
  966. # Return $TRUE if adaptive mode is enabled, $FALSE otherwise
  967. isAdaptiveMode() {
  968. return $ADAPTIVEMODE
  969. }
  970. # Return $TRUE if adaptive mode is disabled, $FALSE otherwise
  971. isNotAdaptiveMode() {
  972. isAdaptiveMode && return $FALSE
  973. return $TRUE
  974. }
  975. ########################## VALIDATORS ##########################
  976. # Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, $FALSE otherwise
  977. pageSizeIsInvalid() {
  978. if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then
  979. return $TRUE
  980. fi
  981. return $FALSE
  982. }
  983. # Return $TRUE if $1 is empty, $FALSE otherwise
  984. isEmpty() {
  985. [[ -z "$1" ]] && return $TRUE
  986. return $FALSE
  987. }
  988. # Return $TRUE if $1 is NOT empty, $FALSE otherwise
  989. isNotEmpty() {
  990. [[ -z "$1" ]] && return $FALSE
  991. return $TRUE
  992. }
  993. # Returns $TRUE if $1 is an integer, $FALSE otherwise
  994. isAnInteger() {
  995. case $1 in
  996. ''|*[!0-9]*) return $FALSE ;;
  997. *) return $TRUE ;;
  998. esac
  999. }
  1000. # Returns $TRUE if $1 is NOT an integer, $FALSE otherwise
  1001. isNotAnInteger() {
  1002. case $1 in
  1003. ''|*[!0-9]*) return $TRUE ;;
  1004. *) return $FALSE ;;
  1005. esac
  1006. }
  1007. # Returns $TRUE if $1 is a floating point number (or an integer), $FALSE otherwise
  1008. isFloat() {
  1009. [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ ]] && return $TRUE
  1010. return $FALSE
  1011. }
  1012. # Returns $TRUE if $1 is a floating point number bigger than zero, $FALSE otherwise
  1013. isFloatBiggerThanZero() {
  1014. isFloat "$1" && [[ (( $1 > 0 )) ]] && return $TRUE
  1015. return $FALSE
  1016. }
  1017. # Returns $TRUE if $1 is readable, $FALSE otherwise
  1018. isReadable() {
  1019. [[ -r "$1" ]] && return $TRUE
  1020. return $FALSE;
  1021. }
  1022. # Returns $TRUE if $1 is a directory, $FALSE otherwise
  1023. isDir() {
  1024. [[ -d "$1" ]] && return $TRUE
  1025. return $FALSE;
  1026. }
  1027. # Returns 0 if succeded, other integer otherwise
  1028. isTouchable() {
  1029. touch "$1" 2>/dev/null
  1030. }
  1031. # Returns $TRUE if $1 has a .pdf extension, false otherwsie
  1032. isPDF() {
  1033. [[ "$(lowercase $1)" =~ ^..*\.pdf$ ]] && return $TRUE
  1034. return $FALSE
  1035. }
  1036. # Returns $TRUE if $1 is a file, false otherwsie
  1037. isFile() {
  1038. [[ -f "$1" ]] && return $TRUE
  1039. return $FALSE
  1040. }
  1041. # Returns $TRUE if $1 is NOT a file, false otherwsie
  1042. isNotFile() {
  1043. [[ -f "$1" ]] && return $FALSE
  1044. return $TRUE
  1045. }
  1046. # Returns $TRUE if $1 is executable, false otherwsie
  1047. isExecutable() {
  1048. [[ -x "$1" ]] && return $TRUE
  1049. return $FALSE
  1050. }
  1051. # Returns $TRUE if $1 is NOT executable, false otherwsie
  1052. isNotExecutable() {
  1053. [[ -x "$1" ]] && return $FALSE
  1054. return $TRUE
  1055. }
  1056. # Returns $TRUE if $1 is a file and executable, false otherwsie
  1057. isAvailable() {
  1058. if isFile "$1" && isExecutable "$1"; then
  1059. return $TRUE
  1060. fi
  1061. return $FALSE
  1062. }
  1063. # Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie
  1064. isNotAvailable() {
  1065. if isNotFile "$1" || isNotExecutable "$1"; then
  1066. return $TRUE
  1067. fi
  1068. return $FALSE
  1069. }
  1070. ###################### PRINTING TO SCREEN ######################
  1071. # Prints version
  1072. printVersion() {
  1073. local vStr=""
  1074. [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution"
  1075. local strBanner="$PDFSCALE_NAME v$VERSION$vStr"
  1076. if [[ $1 -eq 2 ]]; then
  1077. printError "$strBanner"
  1078. elif [[ $1 -eq 3 ]]; then
  1079. local extra="$(isNotEmpty "$2" && echo "$2")"
  1080. echo "$strBanner$extra"
  1081. else
  1082. vprint "$strBanner"
  1083. fi
  1084. }
  1085. # Prints input, output file info, if verbosing
  1086. vPrintFileInfo() {
  1087. vprint " Input File: $INFILEPDF"
  1088. vprint " Output File: $OUTFILEPDF"
  1089. }
  1090. # Prints the scale factor to screen, or custom message
  1091. vPrintScaleFactor() {
  1092. local scaleMsg="$SCALE"
  1093. isNotEmpty "$1" && scaleMsg="$1"
  1094. vprint " Scale Factor: $scaleMsg"
  1095. }
  1096. # Prints help info
  1097. printHelp() {
  1098. printVersion 3
  1099. local paperList="$(printPaperNames)"
  1100. echo "
  1101. Usage: $PDFSCALE_NAME <inFile.pdf>
  1102. $PDFSCALE_NAME -i <inFile.pdf>
  1103. $PDFSCALE_NAME [-v] [-s <factor>] [-m <page-detection>] <inFile.pdf> [outfile.pdf]
  1104. $PDFSCALE_NAME [-v] [-r <paper>] [-f <flip-detection>] [-a <auto-rotation>] <inFile.pdf> [outfile.pdf]
  1105. $PDFSCALE_NAME -p
  1106. $PDFSCALE_NAME -h
  1107. $PDFSCALE_NAME -V
  1108. Parameters:
  1109. -v, --verbose
  1110. Verbose mode, prints extra information
  1111. Use twice for timestamp
  1112. -h, --help
  1113. Print this help to screen and exits
  1114. -V, --version
  1115. Prints version to screen and exits
  1116. -n, --no-overwrite
  1117. Aborts execution if the output PDF file already exists
  1118. By default, the output file will be overwritten
  1119. -m, --mode <mode>
  1120. Paper size detection mode
  1121. Modes: a, adaptive Default mode, tries all the methods below
  1122. g, grep Forces the use of Grep method
  1123. m, mdls Forces the use of MacOS Quartz mdls
  1124. p, pdfinfo Forces the use of PDFInfo
  1125. i, identify Forces the use of ImageMagick's Identify
  1126. -i, --info <file>
  1127. Prints <file> Paper Size information to screen and exits
  1128. -s, --scale <factor>
  1129. Changes the scaling factor or forces mixed mode
  1130. Defaults: $SCALE (scale mode) / Disabled (resize mode)
  1131. MUST be a number bigger than zero
  1132. Eg. -s 0.8 for 80% of the original size
  1133. -r, --resize <paper>
  1134. Triggers the Resize Paper Mode, disables auto-scaling of $SCALE
  1135. Resize PDF and fit-to-page
  1136. <paper> can be: source, custom or a valid std paper name, read below
  1137. -f, --flip-detect <mode>
  1138. Flip Detection Mode, defaults to 'auto'
  1139. Inverts Width <-> Height of a Resized PDF
  1140. Modes: a, auto Keeps source orientation, default
  1141. f, force Forces flip W <-> H
  1142. d, disable Disables flipping
  1143. -a, --auto-rotate <mode>
  1144. Setting for GS -dAutoRotatePages, defaults to 'PageByPage'
  1145. Uses text-orientation detection to set Portrait/Landscape
  1146. Modes: p, pagebypage Auto-rotates pages individually
  1147. n, none Retains orientation of each page
  1148. a, all Rotates all pages (or none) depending
  1149. on a kind of \"majority decision\"
  1150. --image-resolution <dpi>
  1151. Resolution in DPI of color and grayscale images in output
  1152. Default: 300
  1153. -p, --print-papers
  1154. Prints Standard Paper info tables to screen and exits
  1155. Scaling Mode:
  1156. - The default mode of operation is scaling mode with fixed paper
  1157. size and scaling pre-set to $SCALE
  1158. - By not using the resize mode you are using scaling mode
  1159. - Flip-Detection and Auto-Rotation are disabled in Scaling mode,
  1160. you can use '-r source -s <scale>' to override.
  1161. Resize Paper Mode:
  1162. - Disables the default scaling factor! ($SCALE)
  1163. - Changes the PDF Paper Size in points. Will fit-to-page
  1164. Mixed Mode:
  1165. - In mixed mode both the -s option and -r option must be specified
  1166. - The PDF will be first resized then scaled
  1167. Output filename:
  1168. - Having the extension .pdf on the output file name is optional,
  1169. it will be added if not present.
  1170. - The output filename is optional. If no file name is passed
  1171. the output file will have the same name/destination of the
  1172. input file with added suffixes:
  1173. .SCALED.pdf is added to scaled files
  1174. .<PAPERSIZE>.pdf is added to resized files
  1175. .<PAPERSIZE>.SCALED.pdf is added in mixed mode
  1176. Standard Paper Names: (case-insensitive)
  1177. $paperList
  1178. Custom Paper Size:
  1179. - Paper size can be set manually in Milimeters, Inches or Points
  1180. - Custom paper definition MUST be quoted into a single parameter
  1181. - Actual size is applied in points (mms and inches are transformed)
  1182. - Measurements: mm, mms, milimeters
  1183. pt, pts, points
  1184. in, inch, inches
  1185. Use: $PDFSCALE_NAME -r 'custom <measurement> <width> <height>'
  1186. Ex: $PDFSCALE_NAME -r 'custom mm 300 300'
  1187. Using Source Paper Size: (no-resizing)
  1188. - Wildcard 'source' is used used to keep paper size the same as the input
  1189. - Usefull to run Auto-Rotation without resizing
  1190. - Eg. $PDFSCALE_NAME -r source ./input.dpf
  1191. Options and Parameters Parsing:
  1192. - From v2.1.0 (long-opts) there is no need to pass file names at the end
  1193. - Anything that is not a short-option is case-insensitive
  1194. - Short-options: case-sensitive Eg. -v for Verbose, -V for Version
  1195. - Long-options: case-insensitive Eg. --SCALE and --scale are the same
  1196. - Subparameters: case-insensitive Eg. -m PdFinFo is valid
  1197. - Grouping short-options is not supported Eg. -vv, or -vs 0.9
  1198. Additional Notes:
  1199. - File and folder names with spaces should be quoted or escaped
  1200. - The scaling is centered and using a scale bigger than 1.0 may
  1201. result on cropping parts of the PDF
  1202. - For detailed paper types information, use: $PDFSCALE_NAME -p
  1203. Examples:
  1204. $PDFSCALE_NAME myPdfFile.pdf
  1205. $PDFSCALE_NAME -i '/home/My Folder/My PDF File.pdf'
  1206. $PDFSCALE_NAME myPdfFile.pdf \"My Scaled Pdf\"
  1207. $PDFSCALE_NAME -v -v myPdfFile.pdf
  1208. $PDFSCALE_NAME -s 0.85 myPdfFile.pdf My\\ Scaled\\ Pdf.pdf
  1209. $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf
  1210. $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf
  1211. $PDFSCALE_NAME -r A4 myPdfFile.pdf
  1212. $PDFSCALE_NAME -v -v -r \"custom mm 252 356\" -s 0.9 -f \"../input file.pdf\" \"../my new pdf\"
  1213. "
  1214. }
  1215. # Prints usage info
  1216. usage() {
  1217. [[ "$2" != 'nobanner' ]] && printVersion 2
  1218. isNotEmpty "$1" && printError $'\n'"$1"
  1219. printError $'\n'"Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] [-r <paper> [-f <mode>] [-a <mode>]] <inFile.pdf> [outfile.pdf]"
  1220. printError "Help : $PDFSCALE_NAME -h"
  1221. }
  1222. # Prints Verbose information
  1223. vprint() {
  1224. [[ $VERBOSE -eq 0 ]] && return $TRUE
  1225. timestamp=""
  1226. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  1227. echo "$timestamp$1"
  1228. }
  1229. # Prints dependency information and aborts execution
  1230. printDependency() {
  1231. printVersion 2
  1232. local brewName="$1"
  1233. [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf"
  1234. printError $'\n'"ERROR! You need to install the package '$1'"$'\n'
  1235. printError "Linux apt-get.: sudo apt-get install $1"
  1236. printError "Linux yum.....: sudo yum install $1"
  1237. printError "MacOS homebrew: brew install $brewName"
  1238. printError $'\n'"Aborting..."
  1239. exit $EXIT_MISSING_DEPENDENCY
  1240. }
  1241. # Prints initialization errors and aborts execution
  1242. initError() {
  1243. local errStr="$1"
  1244. local exitStat=$2
  1245. isEmpty "$exitStat" && exitStat=$EXIT_ERROR
  1246. usage "ERROR! $errStr" "$3"
  1247. exit $exitStat
  1248. }
  1249. # Prints to stderr
  1250. printError() {
  1251. echo >&2 "$@"
  1252. }
  1253. ########################## EXECUTION ###########################
  1254. initDeps
  1255. getOptions "${@}"
  1256. main
  1257. exit $?