Bash Script to scale and/or resize PDFs from the command line.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

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