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.
 
 

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