Bash Script to scale and/or resize PDFs from the command line.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

1023 行
28 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. #
  8. # This script: https://github.com/tavinus/pdfScale
  9. # Based on: http://ma.juii.net/blog/scale-page-content-of-pdf-files
  10. # And: https://gist.github.com/MichaelJCole/86e4968dbfc13256228a
  11. ###################################################
  12. # PAGESIZE LOGIC
  13. # 1- Try to get Mediabox with CAT/GREP
  14. # 2- MacOS => try to use mdls
  15. # Linux => try to use pdfinfo
  16. # 3- Try to use identify (imagemagick)
  17. # 4- Fail
  18. # Remove postscript method,
  19. # may have licensing problems
  20. ###################################################
  21. VERSION="2.0.0"
  22. SCALE="0.95" # scaling factor (0.95 = 95%, e.g.)
  23. VERBOSE=0 # verbosity Level
  24. PDFSCALE_NAME="$(basename $0)" # simplified name of this script
  25. # Set with which later
  26. GSBIN="" # GhostScript Binary
  27. BCBIN="" # BC Math Binary
  28. IDBIN="" # Identify Binary
  29. PDFINFOBIN="" # PDF Info Binary
  30. MDLSBIN="" # MacOS mdls Binary
  31. OSNAME="$(uname 2>/dev/null)" # Check where we are running
  32. LC_MEASUREMENT="C" # To make sure our numbers have .decimals
  33. LC_ALL="C" # Some languages use , as decimal token
  34. LC_CTYPE="C"
  35. LC_NUMERIC="C"
  36. TRUE=0 # Silly stuff
  37. FALSE=1
  38. ADAPTIVEMODE=$TRUE # Automatically try to guess best mode
  39. AUTOMATIC_SCALING=$TRUE # Default scaling in $SCALE, override by resize mode
  40. MODE=""
  41. RESIZE_PAPER_TYPE=""
  42. PGWIDTH=""
  43. PGHEIGHT=""
  44. RESIZE_WIDTH=""
  45. RESIZE_HEIGHT=""
  46. # Exit flags
  47. EXIT_SUCCESS=0
  48. EXIT_ERROR=1
  49. EXIT_INVALID_PAGE_SIZE_DETECTED=10
  50. EXIT_FILE_NOT_FOUND=20
  51. EXIT_INPUT_NOT_PDF=21
  52. EXIT_INVALID_OPTION=22
  53. EXIT_NO_INPUT_FILE=23
  54. EXIT_INVALID_SCALE=24
  55. EXIT_MISSING_DEPENDENCY=25
  56. EXIT_IMAGEMAGIK_NOT_FOUND=26
  57. EXIT_MAC_MDLS_NOT_FOUND=27
  58. EXIT_PDFINFO_NOT_FOUND=28
  59. EXIT_INVALID_PAPER_SIZE=50
  60. # Parses and validates the scaling factor
  61. parseScale() {
  62. AUTOMATIC_SCALING=$FALSE
  63. if ! [[ -n "$1" && "$1" =~ ^-?[0-9]*([.][0-9]+)?$ && (($1 > 0 )) ]] ; then
  64. printError "Invalid factor: $1"
  65. printError "The factor must be a floating point number greater than 0"
  66. printError "Example: for 80% use 0.8"
  67. exit $EXIT_INVALID_SCALE
  68. fi
  69. SCALE=$1
  70. }
  71. # Parse a forced mode of operation
  72. parseMode() {
  73. if [[ -z $1 ]]; then
  74. printError "Mode is empty, please specify the desired mode"
  75. printError "Falling back to adaptive mode!"
  76. ADAPTIVEMODE=$TRUE
  77. MODE=""
  78. return $FALSE
  79. fi
  80. if [[ $1 = 'c' || $1 = 'catgrep' || $1 = 'cat+grep' || $1 = 'CatGrep' || $1 = 'C' || $1 = 'CATGREP' ]]; then
  81. ADAPTIVEMODE=$FALSE
  82. MODE="CATGREP"
  83. return $TRUE
  84. elif [[ $1 = 'i' || $1 = 'imagemagick' || $1 = 'identify' || $1 = 'ImageMagick' || $1 = 'Identify' || $1 = 'I' || $1 = 'IDENTIFY' ]]; then
  85. ADAPTIVEMODE=$FALSE
  86. MODE="IDENTIFY"
  87. return $TRUE
  88. elif [[ $1 = 'm' || $1 = 'mdls' || $1 = 'MDLS' || $1 = 'quartz' || $1 = 'mac' || $1 = 'M' ]]; then
  89. ADAPTIVEMODE=$FALSE
  90. MODE="MDLS"
  91. return $TRUE
  92. elif [[ $1 = 'p' || $1 = 'pdfinfo' || $1 = 'PDFINFO' || $1 = 'PdfInfo' || $1 = 'P' ]]; then
  93. ADAPTIVEMODE=$FALSE
  94. MODE="PDFINFO"
  95. return $TRUE
  96. elif [[ $1 = 'a' || $1 = 'adaptive' || $1 = 'automatic' || $1 = 'A' || $1 = 'ADAPTIVE' || $1 = 'AUTOMATIC' ]]; then
  97. ADAPTIVEMODE=$TRUE
  98. MODE=""
  99. return $TRUE
  100. else
  101. printError "Invalid mode: $1"
  102. printError "Falling back to adaptive mode!"
  103. ADAPTIVEMODE=$TRUE
  104. MODE=""
  105. return $FALSE
  106. fi
  107. return $FALSE
  108. }
  109. # Gets page size using imagemagick's identify
  110. getPageSizeImagemagick() {
  111. # Sanity and Adaptive together
  112. if notIsFile "$IDBIN" && isNotAdaptiveMode; then
  113. notAdaptiveFailed "Make sure you installed ImageMagick and have identify on your \$PATH" "ImageMagick's Identify"
  114. elif notIsFile "$IDBIN" && isAdaptiveMode; then
  115. return $FALSE
  116. fi
  117. # get data from image magick
  118. local identify="$("$IDBIN" -format '%[fx:w] %[fx:h]BREAKME' "$INFILEPDF" 2>/dev/null)"
  119. if isEmpty "$identify" && isNotAdaptiveMode; then
  120. notAdaptiveFailed "ImageMagicks's Identify returned an empty string!"
  121. elif isEmpty "$identify" && isAdaptiveMode; then
  122. return $FALSE
  123. fi
  124. identify="${identify%%BREAKME*}" # get page size only for 1st page
  125. identify=($identify) # make it an array
  126. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  127. PGHEIGHT=$(printf '%.0f' "${identify[1]}") # assign
  128. return $TRUE
  129. }
  130. # Gets page size using Mac Quarts mdls
  131. getPageSizeMdls() {
  132. # Sanity and Adaptive together
  133. if notIsFile "$MDLSBIN" && isNotAdaptiveMode; then
  134. notAdaptiveFailed "Are you even trying this on a Mac?" "Mac Quartz mdls"
  135. elif notIsFile "$MDLSBIN" && isAdaptiveMode; then
  136. return $FALSE
  137. fi
  138. local identify="$("$MDLSBIN" -mdls -name kMDItemPageHeight -name kMDItemPageWidth "$INFILEPDF" 2>/dev/null)"
  139. if isEmpty "$identify" && isNotAdaptiveMode; then
  140. notAdaptiveFailed "Mac Quartz mdls returned an empty string!"
  141. elif isEmpty "$identify" && isAdaptiveMode; then
  142. return $FALSE
  143. fi
  144. identify=${identify//$'\t'/ } # change tab to space
  145. identify=($identify) # make it an array
  146. if [[ "${identify[5]}" = "(null)" || "${identify[5]}" = "(null)" ]] && isNotAdaptiveMode; then
  147. notAdaptiveFailed "There was no metadata to read from the file! Is Spotlight OFF?"
  148. elif [[ "${identify[5]}" = "(null)" || "${identify[5]}" = "(null)" ]] && isAdaptiveMode; then
  149. return $FALSE
  150. fi
  151. PGWIDTH=$(printf '%.0f' "${identify[5]}") # assign
  152. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  153. return $TRUE
  154. }
  155. # Gets page size using Linux PdfInfo
  156. getPageSizePdfInfo() {
  157. # Sanity and Adaptive together
  158. if notIsFile "$PDFINFOBIN" && isNotAdaptiveMode; then
  159. notAdaptiveFailed "Do you have pdfinfo installed and available on your \$PATH?" "Linux pdfinfo"
  160. elif notIsFile "$PDFINFOBIN" && isAdaptiveMode; then
  161. return $FALSE
  162. fi
  163. # get data from image magick
  164. local identify="$("$PDFINFOBIN" "$INFILEPDF" 2>/dev/null | grep -i 'Page size:' )"
  165. if isEmpty "$identify" && isNotAdaptiveMode; then
  166. notAdaptiveFailed "Linux PdfInfo returned an empty string!"
  167. elif isEmpty "$identify" && isAdaptiveMode; then
  168. return $FALSE
  169. fi
  170. identify="${identify##*Page size:}" # remove stuff
  171. identify=($identify) # make it an array
  172. PGWIDTH=$(printf '%.0f' "${identify[0]}") # assign
  173. PGHEIGHT=$(printf '%.0f' "${identify[2]}") # assign
  174. return $TRUE
  175. }
  176. # Gets page size using cat and grep
  177. getPageSizeCatGrep() {
  178. # get MediaBox info from PDF file using cat and grep, these are all possible
  179. # /MediaBox [0 0 595 841]
  180. # /MediaBox [ 0 0 595.28 841.89]
  181. # /MediaBox[ 0 0 595.28 841.89 ]
  182. # Get MediaBox data if possible
  183. local mediaBox="$(cat "$INFILEPDF" | grep -a '/MediaBox' | head -n1)"
  184. mediaBox="${mediaBox##*/MediaBox}"
  185. # No page size data available
  186. if isEmpty "$mediaBox" && isNotAdaptiveMode; then
  187. notAdaptiveFailed "There is no MediaBox in the pdf document!"
  188. elif isEmpty "$mediaBox" && isAdaptiveMode; then
  189. return $FALSE
  190. fi
  191. # remove chars [ and ]
  192. mediaBox="${mediaBox//[}"
  193. mediaBox="${mediaBox//]}"
  194. mediaBox=($mediaBox) # make it an array
  195. mbCount=${#mediaBox[@]} # array size
  196. # sanity
  197. if [[ $mbCount -lt 4 ]]; then
  198. printError "Error when reading the page size!"
  199. printError "The page size information is invalid!"
  200. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  201. fi
  202. # we are done
  203. PGWIDTH=$(printf '%.0f' "${mediaBox[2]}") # Get Round Width
  204. PGHEIGHT=$(printf '%.0f' "${mediaBox[3]}") # Get Round Height
  205. return $TRUE
  206. }
  207. # Prints error message and exits execution
  208. notAdaptiveFailed() {
  209. local errProgram="$2"
  210. local errStr="$1"
  211. if isEmpty "$2"; then
  212. printError "Error when reading input file!"
  213. printError "Could not determine the page size!"
  214. else
  215. printError "Error! $2 was not found!"
  216. fi
  217. isNotEmpty "$errStr" && printError "$errStr"
  218. printError "Aborting! You may want to try the adaptive mode."
  219. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  220. }
  221. # Return $TRUE if adaptive mode is enabled, false otherwise
  222. isAdaptiveMode() {
  223. return $ADAPTIVEMODE
  224. }
  225. # Return $TRUE if adaptive mode is disabled, false otherwise
  226. isNotAdaptiveMode() {
  227. isAdaptiveMode && return $FALSE
  228. return $TRUE
  229. }
  230. # Return $TRUE if $1 is empty, false otherwise
  231. isEmpty() {
  232. [[ -z "$1" ]] && return $TRUE
  233. return $FALSE
  234. }
  235. # Return $TRUE if $1 is NOT empty, false otherwise
  236. isNotEmpty() {
  237. [[ -z "$1" ]] && return $FALSE
  238. return $TRUE
  239. }
  240. # Detects operation mode and also runs the adaptive mode
  241. getPageSize() {
  242. if isNotAdaptiveMode; then
  243. vprint " Get Page Size: Adaptive Disabled"
  244. if [[ $MODE = "CATGREP" ]]; then
  245. vprint " Method: Cat + Grep"
  246. getPageSizeCatGrep
  247. elif [[ $MODE = "MDLS" ]]; then
  248. vprint " Method: Mac Quartz mdls"
  249. getPageSizeMdls
  250. elif [[ $MODE = "PDFINFO" ]]; then
  251. vprint " Method: PDFInfo"
  252. getPageSizePdfInfo
  253. elif [[ $MODE = "IDENTIFY" ]]; then
  254. vprint " Method: ImageMagick's Identify"
  255. getPageSizeImagemagick
  256. else
  257. printError "Error! Invalid Mode: $MODE"
  258. printError "Aborting execution..."
  259. exit $EXIT_INVALID_OPTION
  260. fi
  261. return $TRUE
  262. fi
  263. vprint " Get Page Size: Adaptive Enabled"
  264. vprint " Method: Cat + Grep"
  265. getPageSizeCatGrep
  266. if pageSizeIsInvalid && [[ $OSNAME = "Darwin" ]]; then
  267. vprint " Failed"
  268. vprint " Method: Mac Quartz mdls"
  269. getPageSizeMdls
  270. fi
  271. if pageSizeIsInvalid; then
  272. vprint " Failed"
  273. vprint " Method: PDFInfo"
  274. getPageSizePdfInfo
  275. fi
  276. if pageSizeIsInvalid; then
  277. vprint " Failed"
  278. vprint " Method: ImageMagick's Identify"
  279. getPageSizeImagemagick
  280. fi
  281. if pageSizeIsInvalid; then
  282. vprint " Failed"
  283. printError "Error when detecting PDF paper size!"
  284. printError "All methods of detection failed"
  285. printError "You may want to install pdfinfo or imagemagick"
  286. exit $EXIT_INVALID_PAGE_SIZE_DETECTED
  287. fi
  288. return $TRUE
  289. }
  290. vPrintSourcePageSizes() {
  291. vprint " $1 Width: $PGWIDTH postscript-points"
  292. vprint "$1 Height: $PGHEIGHT postscript-points"
  293. }
  294. # Returns $TRUE if $PGWIDTH OR $PGWIDTH are empty or NOT an Integer, false otherwise
  295. pageSizeIsInvalid() {
  296. if isNotAnInteger "$PGWIDTH" || isNotAnInteger "$PGHEIGHT"; then
  297. return $TRUE
  298. fi
  299. return $FALSE
  300. }
  301. isAnInteger() {
  302. case $1 in
  303. ''|*[!0-9]*) return $FALSE ;;
  304. *) return $TRUE ;;
  305. esac
  306. }
  307. isNotAnInteger() {
  308. case $1 in
  309. ''|*[!0-9]*) return $TRUE ;;
  310. *) return $FALSE ;;
  311. esac
  312. }
  313. # Prints usage info
  314. usage() {
  315. [[ "$2" != 'nobanner' ]] && printVersion 2
  316. [[ ! -z "$1" ]] && printError "$1"
  317. printError "Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] <inFile.pdf> [outfile.pdf]"
  318. printError "Try: $PDFSCALE_NAME -h # for help"
  319. }
  320. # Prints Verbose information
  321. vprint() {
  322. [[ $VERBOSE -eq 0 ]] && return 0
  323. timestamp=""
  324. [[ $VERBOSE -gt 1 ]] && timestamp="$(date +%Y-%m-%d:%H:%M:%S) | "
  325. echo "$timestamp$1"
  326. }
  327. # Prints dependency information and aborts execution
  328. printDependency() {
  329. #printVersion 2
  330. local brewName="$1"
  331. [[ "$1" = 'pdfinfo' && "$OSNAME" = "Darwin" ]] && brewName="xpdf"
  332. printError $'\n'"ERROR! You need to install the package '$1'"$'\n'
  333. printError "Linux apt-get.: sudo apt-get install $1"
  334. printError "Linux yum.....: sudo yum install $1"
  335. printError "MacOS homebrew: brew install $brewName"
  336. printError $'\n'"Aborting..."
  337. exit $EXIT_MISSING_DEPENDENCY
  338. }
  339. # Prints initialization errors and aborts execution
  340. initError() {
  341. local errStr="$1"
  342. local exitStat=$2
  343. [[ -z "$exitStat" ]] && exitStat=$EXIT_ERROR
  344. usage "ERROR! $errStr" "$3"
  345. exit $exitStat
  346. }
  347. # Prints to stderr
  348. printError() {
  349. echo >&2 "$@"
  350. }
  351. # Returns $TRUE if $1 has a .pdf extension, false otherwsie
  352. isPDF() {
  353. [[ "$1" =~ ^..*\.pdf$ ]] && return $TRUE
  354. return $FALSE
  355. }
  356. # Returns $TRUE if $1 is a file, false otherwsie
  357. isFile() {
  358. [[ -f "$1" ]] && return $TRUE
  359. return $FALSE
  360. }
  361. # Returns $TRUE if $1 is NOT a file, false otherwsie
  362. notIsFile() {
  363. [[ -f "$1" ]] && return $FALSE
  364. return $TRUE
  365. }
  366. # Returns $TRUE if $1 is executable, false otherwsie
  367. isExecutable() {
  368. [[ -x "$1" ]] && return $TRUE
  369. return $FALSE
  370. }
  371. # Returns $TRUE if $1 is NOT executable, false otherwsie
  372. notIsExecutable() {
  373. [[ -x "$1" ]] && return $FALSE
  374. return $TRUE
  375. }
  376. # Returns $TRUE if $1 is a file and executable, false otherwsie
  377. isAvailable() {
  378. if isFile "$1" && isExecutable "$1"; then
  379. return $TRUE
  380. fi
  381. return $FALSE
  382. }
  383. # Returns $TRUE if $1 is NOT a file or NOT executable, false otherwsie
  384. notIsAvailable() {
  385. if notIsFile "$1" || notIsExecutable "$1"; then
  386. return $TRUE
  387. fi
  388. return $FALSE
  389. }
  390. # Loads external dependencies and checks for errors
  391. loadDeps() {
  392. GSBIN="$(which gs 2>/dev/null)"
  393. BCBIN="$(which bc 2>/dev/null)"
  394. IDBIN=$(which identify 2>/dev/null)
  395. MDLSBIN="$(which mdls 2>/dev/null)"
  396. PDFINFOBIN="$(which pdfinfo 2>/dev/null)"
  397. vprint "Checking for ghostscript and bcmath"
  398. if notIsAvailable "$GSBIN"; then printDependency 'ghostscript'; fi
  399. if notIsAvailable "$BCBIN"; then printDependency 'bc'; fi
  400. if [[ $MODE = "IDENTIFY" ]]; then
  401. vprint "Checking for imagemagick's identify"
  402. if notIsAvailable "$IDBIN"; then printDependency 'imagemagick'; fi
  403. fi
  404. if [[ $MODE = "PDFINFO" ]]; then
  405. vprint "Checking for pdfinfo"
  406. if notIsAvailable "$PDFINFOBIN"; then printDependency 'pdfinfo'; fi
  407. fi
  408. if [[ $MODE = "MDLS" ]]; then
  409. vprint "Checking for MacOS mdls"
  410. if notIsAvailable "$MDLSBIN"; then
  411. initError 'mdls executable was not found! Is this even MacOS?' $EXIT_MAC_MDLS_NOT_FOUND 'nobanner'
  412. fi
  413. fi
  414. }
  415. # Main execution
  416. main() {
  417. printVersion 1 'verbose'
  418. #getScaledOutputName
  419. #Intro message
  420. #vprint "$(basename $0) v$VERSION - Verbose execution"
  421. loadDeps
  422. vprint " Input file: $INFILEPDF"
  423. vprint " Output file: $OUTFILEPDF"
  424. getPageSize
  425. if isMixedMode; then
  426. vprint " Mixed Tasks: Resize & Scale"
  427. vprint " Scale factor: $SCALE"
  428. vPrintSourcePageSizes ' Source'
  429. outputFile="$OUTFILEPDF" # backup outFile name
  430. tempFile="${OUTFILEPDF%.pdf}.__TEMP__.pdf" # set a temp file name
  431. OUTFILEPDF="$tempFile" # set output to tmp file
  432. pageResize # resize to tmp file
  433. INFILEPDF="$tempFile" # get tmp file as input
  434. OUTFILEPDF="$outputFile" # reset final target
  435. PGWIDTH=$RESIZE_WIDTH # we already know the new page size
  436. PGHEIGHT=$RESIZE_HEIGHT # from the last command (Resize)
  437. vPrintSourcePageSizes ' New'
  438. pageScale # scale the resized pdf
  439. # remove tmp file
  440. rm "$tempFile" >/dev/null 2>&1 || printError "Error when removing temporary file: $tempFile"
  441. elif isResizeMode; then
  442. vprint " Single Task: Resize PDF Paper"
  443. vprint " Scale factor: Disabled (resize only)"
  444. vPrintSourcePageSizes ' Source'
  445. pageResize
  446. else
  447. local scaleMode=""
  448. vprint " Single Task: Scale PDF Contents"
  449. isManualScaledMode && scaleMode='(manual)' || scaleMode='(auto)'
  450. vprint " Scale factor: $SCALE $scaleMode"
  451. vPrintSourcePageSizes ' Source'
  452. pageScale
  453. fi
  454. #pageScale
  455. #pageResize
  456. }
  457. # Parse options
  458. getOptions() {
  459. while getopts ":vhVs:m:r:p" o; do
  460. case "${o}" in
  461. v)
  462. ((VERBOSE++))
  463. ;;
  464. h)
  465. printHelp
  466. exit $EXIT_SUCCESS
  467. ;;
  468. V)
  469. printVersion
  470. exit $EXIT_SUCCESS
  471. ;;
  472. s)
  473. parseScale ${OPTARG}
  474. ;;
  475. m)
  476. parseMode ${OPTARG}
  477. ;;
  478. r)
  479. parsePaperResize ${OPTARG}
  480. ;;
  481. p)
  482. printPaperInfo
  483. exit $EXIT_SUCCESS
  484. ;;
  485. *)
  486. initError "Invalid Option: -$OPTARG" $EXIT_INVALID_OPTION
  487. ;;
  488. esac
  489. done
  490. shift $((OPTIND-1))
  491. # Validate input PDF file
  492. INFILEPDF="$1"
  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. if isEmpty "$2"; then
  497. if isMixedMode; then
  498. OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).SCALED.pdf"
  499. elif isResizeMode; then
  500. OUTFILEPDF="${INFILEPDF%.pdf}.$(uppercase $RESIZE_PAPER_TYPE).pdf"
  501. else
  502. OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf"
  503. fi
  504. else
  505. OUTFILEPDF="${2%.pdf}.pdf"
  506. fi
  507. }
  508. # Runs the ghostscript scaling script
  509. pageScale() {
  510. # Compute translation factors (to center page).
  511. XTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGWIDTH" | "$BCBIN")
  512. YTRANS=$(echo "scale=6; 0.5*(1.0-$SCALE)/$SCALE*$PGHEIGHT" | "$BCBIN")
  513. vprint " Translation X: $XTRANS"
  514. vprint " Translation Y: $YTRANS"
  515. # Do it.
  516. "$GSBIN" \
  517. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  518. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  519. -dColorConversionStrategy=/LeaveColorUnchanged \
  520. -dSubsetFonts=true -dEmbedAllFonts=true \
  521. -dDEVICEWIDTHPOINTS=$PGWIDTH -dDEVICEHEIGHTPOINTS=$PGHEIGHT \
  522. -sOutputFile="$OUTFILEPDF" \
  523. -c "<</BeginPage{$SCALE $SCALE scale $XTRANS $YTRANS translate}>> setpagedevice" \
  524. -f "$INFILEPDF" &
  525. wait ${!}
  526. }
  527. pageResize() {
  528. getGSPaperSize "$RESIZE_PAPER_TYPE"
  529. local tmpInverter=""
  530. if [[ $PGWIDTH -gt $PGHEIGHT && $RESIZE_WIDTH -lt $RESIZE_HEIGHT ]]; then
  531. vprint " Flip Detect: Wrong orientation!"
  532. vprint " Inverting Width <-> Height"
  533. tmpInverter=$RESIZE_HEIGHT
  534. RESIZE_HEIGHT=$RESIZE_WIDTH
  535. RESIZE_WIDTH=$tmpInverter
  536. fi
  537. vprint " Resizing to: $(uppercase $RESIZE_PAPER_TYPE) ( $RESIZE_WIDTH x $RESIZE_HEIGHT )"
  538. # Change page size
  539. "$GSBIN" \
  540. -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER \
  541. -dCompatibilityLevel="1.5" -dPDFSETTINGS="/printer" \
  542. -dColorConversionStrategy=/LeaveColorUnchanged \
  543. -dSubsetFonts=true -dEmbedAllFonts=true \
  544. -dDEVICEWIDTHPOINTS=$RESIZE_WIDTH -dDEVICEHEIGHTPOINTS=$RESIZE_HEIGHT \
  545. -dFIXEDMEDIA -dPDFFitPage \
  546. -sOutputFile="$OUTFILEPDF" \
  547. -f "$INFILEPDF" &
  548. wait ${!}
  549. }
  550. # Automatic name for SCALED pdf
  551. getOutputName() {
  552. # Parse output filename for scaled files
  553. if [[ -z $OUTFILEPDF ]]; then
  554. OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf"
  555. else
  556. OUTFILEPDF="${OUTFILEPDF%.pdf}.pdf"
  557. fi
  558. }
  559. # Automatic name for SCALED pdf
  560. getScaledOutputName() {
  561. # Parse output filename for scaled files
  562. if [[ -z $OUTFILEPDF ]]; then
  563. OUTFILEPDF="${INFILEPDF%.pdf}.SCALED.pdf"
  564. else
  565. OUTFILEPDF="${OUTFILEPDF%.pdf}.pdf"
  566. fi
  567. }
  568. # Automatica name for RESIZED pdf
  569. getResizedOutputName() {
  570. # Parse output filename for scaled files
  571. if [[ -z $OUTFILEPDF ]]; then
  572. OUTFILEPDF="${INFILEPDF%.pdf}.$PAPER_RESIZE.pdf"
  573. else
  574. OUTFILEPDF="${OUTFILEPDF%.pdf}.pdf"
  575. fi
  576. }
  577. getPaperInfo() {
  578. # name inchesW inchesH mmW mmH pointsW pointsH
  579. sizesUS="\
  580. 11x17 11.0 17.0 279 432 792 1224
  581. ledger 17.0 11.0 432 279 1224 792
  582. legal 8.5 14.0 216 356 612 1008
  583. letter 8.5 11.0 216 279 612 792
  584. lettersmall 8.5 11.0 216 279 612 792
  585. archE 36.0 48.0 914 1219 2592 3456
  586. archD 24.0 36.0 610 914 1728 2592
  587. archC 18.0 24.0 457 610 1296 1728
  588. archB 12.0 18.0 305 457 864 1296
  589. archA 9.0 12.0 229 305 648 864"
  590. sizesISO="\
  591. a0 33.1 46.8 841 1189 2384 3370
  592. a1 23.4 33.1 594 841 1684 2384
  593. a2 16.5 23.4 420 594 1191 1684
  594. a3 11.7 16.5 297 420 842 1191
  595. a4 8.3 11.7 210 297 595 842
  596. a4small 8.3 11.7 210 297 595 842
  597. a5 5.8 8.3 148 210 420 595
  598. a6 4.1 5.8 105 148 297 420
  599. a7 2.9 4.1 74 105 210 297
  600. a8 2.1 2.9 52 74 148 210
  601. a9 1.5 2.1 37 52 105 148
  602. a10 1.0 1.5 26 37 73 105
  603. isob0 39.4 55.7 1000 1414 2835 4008
  604. isob1 27.8 39.4 707 1000 2004 2835
  605. isob2 19.7 27.8 500 707 1417 2004
  606. isob3 13.9 19.7 353 500 1001 1417
  607. isob4 9.8 13.9 250 353 709 1001
  608. isob5 6.9 9.8 176 250 499 709
  609. isob6 4.9 6.9 125 176 354 499
  610. c0 36.1 51.1 917 1297 2599 3677
  611. c1 25.5 36.1 648 917 1837 2599
  612. c2 18.0 25.5 458 648 1298 1837
  613. c3 12.8 18.0 324 458 918 1298
  614. c4 9.0 12.8 229 324 649 918
  615. c5 6.4 9.0 162 229 459 649
  616. c6 4.5 6.4 114 162 323 459"
  617. sizesJIS="\
  618. jisb0 NA NA 1030 1456 NA NA
  619. jisb1 NA NA 728 1030 NA NA
  620. jisb2 NA NA 515 728 NA NA
  621. jisb3 NA NA 364 515 NA NA
  622. jisb4 NA NA 257 364 NA NA
  623. jisb5 NA NA 182 257 NA NA
  624. jisb6 NA NA 128 182 NA NA"
  625. sizesOther="\
  626. flsa 8.5 13.0 216 330 612 936
  627. flse 8.5 13.0 216 330 612 936
  628. halfletter 5.5 8.5 140 216 396 612
  629. hagaki 3.9 5.8 100 148 283 420"
  630. sizesAll="\
  631. $sizesUS
  632. $sizesISO
  633. $sizesJIS
  634. $sizesOther"
  635. }
  636. getGSPaperSize() {
  637. isEmpty "$sizesall" && getPaperInfo
  638. while read l; do
  639. local cols=($l)
  640. if [[ "$1" == ${cols[0]} ]]; then
  641. RESIZE_WIDTH=${cols[5]}
  642. RESIZE_HEIGHT=${cols[6]}
  643. return $TRUE
  644. fi
  645. done <<< "$sizesAll"
  646. }
  647. getPaperNames() {
  648. 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 \
  649. 11x17 ledger legal letter lettersmall archE archD archC archB archA \
  650. jisb0 jisb1 jisb2 jisb3 jisb4 jisb5 jisb6 \
  651. flsa flse halfletter hagaki)
  652. }
  653. printPaperNames() {
  654. isEmpty "$paperNames" && getPaperNames
  655. for i in "${!paperNames[@]}"; do
  656. [[ $i -ne 0 && $((i % 5)) -eq 0 ]] && echo ""
  657. ppN="$(uppercase ${paperNames[i]})"
  658. printf "%-14s" "$ppN"
  659. done
  660. echo ""
  661. }
  662. isPaperName() {
  663. isEmpty "$1" && return $FALSE
  664. isEmpty "$paperNames" && getPaperNames
  665. for i in "${paperNames[@]}"; do
  666. [[ "$i" = "$1" ]] && return $TRUE
  667. done
  668. return $FALSE
  669. }
  670. printPaperInfo() {
  671. printVersion
  672. echo $'\n'"Valid Ghostscript Paper Sizes accepted"$'\n'
  673. getPaperInfo
  674. printPaperTable "ISO STANDARD" "$sizesISO"; echo
  675. printPaperTable "US STANDARD" "$sizesUS"; echo
  676. printPaperTable "JIS STANDARD" "$sizesJIS"; echo
  677. printPaperTable "OTHERS" "$sizesOther"; echo
  678. }
  679. printTableLine() {
  680. echo '+-----------------------------------------------------------------+'
  681. }
  682. printTableDivider() {
  683. echo '+-----------------+-------+-------+-------+-------+-------+-------+'
  684. }
  685. printTableHeader() {
  686. echo '| Name | inchW | inchH | mm W | mm H | pts W | pts H |'
  687. }
  688. printTableTitle() {
  689. printf "| %-64s%s\n" "$1" '|'
  690. }
  691. printPaperTable() {
  692. printTableLine
  693. printTableTitle "$1"
  694. printTableLine
  695. printTableHeader
  696. printTableDivider
  697. while read l; do
  698. local cols=($l)
  699. printf "| %-15s | %+5s | %+5s | %+5s | %+5s | %+5s | %+5s |\n" ${cols[*]};
  700. done <<< "$2"
  701. printTableDivider
  702. }
  703. parsePaperResize() {
  704. isEmpty "$1" && initError 'Invalid Paper Type: (empty)' $EXIT_INVALID_PAPER_SIZE
  705. local lowercasePaper="$(lowercase $1)"
  706. ! isPaperName "$lowercasePaper" && initError "Invalid Paper Type: $1" $EXIT_INVALID_PAPER_SIZE
  707. RESIZE_PAPER_TYPE="$lowercasePaper"
  708. }
  709. isManualScaledMode() {
  710. [[ $AUTOMATIC_SCALING -eq $TRUE ]] && return $FALSE
  711. return $TRUE
  712. }
  713. isResizeMode() {
  714. isEmpty $RESIZE_PAPER_TYPE && return $FALSE
  715. return $TRUE
  716. }
  717. isMixedMode() {
  718. isResizeMode && isManualScaledMode && return $TRUE
  719. return $FALSE
  720. }
  721. lowercaseChar() {
  722. case "$1" in
  723. [A-Z])
  724. n=$(printf "%d" "'$1")
  725. n=$((n+32))
  726. printf \\$(printf "%o" "$n")
  727. ;;
  728. *)
  729. printf "%s" "$1"
  730. ;;
  731. esac
  732. }
  733. lowercase() {
  734. word="$@"
  735. for((i=0;i<${#word};i++))
  736. do
  737. ch="${word:$i:1}"
  738. lowercaseChar "$ch"
  739. done
  740. }
  741. uppercaseChar(){
  742. case "$1" in
  743. [a-z])
  744. n=$(printf "%d" "'$1")
  745. n=$((n-32))
  746. printf \\$(printf "%o" "$n")
  747. ;;
  748. *)
  749. printf "%s" "$1"
  750. ;;
  751. esac
  752. }
  753. uppercase() {
  754. word="$@"
  755. for((i=0;i<${#word};i++))
  756. do
  757. ch="${word:$i:1}"
  758. uppercaseChar "$ch"
  759. done
  760. }
  761. #printPaperInfo
  762. #printPaperNames
  763. #echo "----"
  764. #isPaperName a4s; echo $?
  765. ####----------Print-Program-Information----------####
  766. # Prints version
  767. printVersion() {
  768. local vStr=""
  769. [[ "$2" = 'verbose' ]] && vStr=" - Verbose Execution"
  770. if [[ $1 -eq 2 ]]; then
  771. printError "$PDFSCALE_NAME v$VERSION$vStr"
  772. else
  773. echo "$PDFSCALE_NAME v$VERSION$vStr"
  774. fi
  775. }
  776. # Prints help info
  777. printHelp() {
  778. printVersion
  779. local paperList="$(printPaperNames)"
  780. echo "
  781. Usage: $PDFSCALE_NAME [-v] [-s <factor>] [-m <mode>] [-r <paper>] <inFile.pdf> [outfile.pdf]
  782. $PDFSCALE_NAME -p
  783. $PDFSCALE_NAME -h
  784. $PDFSCALE_NAME -V
  785. Parameters:
  786. -v Verbose mode, prints extra information
  787. Use twice for timestamp
  788. -h Print this help to screen and exits
  789. -V Prints version to screen and exits
  790. -m <mode> Page size Detection mode
  791. May disable the Adaptive Mode
  792. -s <factor> Changes the scaling factor or forces scaling
  793. Defaults: $SCALE / no scaling (resize mode)
  794. MUST be a number bigger than zero
  795. Eg. -s 0.8 for 80% of the original size
  796. -r <paper> Triggers the Resize Paper Mode
  797. Resize PDF paper proportionally
  798. Must be a valid Ghostscript paper name
  799. -p Prints Ghostscript paper info tables to screen
  800. Scaling Mode:
  801. The default mode of operation is scaling mode with fixed paper
  802. size and scaling pre-set to $SCALE. By not using the resize mode
  803. you are using scaling mode.
  804. Resize Paper Mode:
  805. Disables the default scaling factor! ($SCALE)
  806. Alternative mode of operation to change the PDF paper
  807. proportionally. Will fit-to-page.
  808. Mixed Mode:
  809. In mixed mode both the -s option and -r option must be specified.
  810. The PDF will be both scaled and have the paper type changed.
  811. Output filename:
  812. The output filename is optional. If no file name is passed
  813. the output file will have the same name/destination of the
  814. input file with added suffixes:
  815. .SCALED.pdf is added to scaled files
  816. .<PAPERSIZE>.pdf is added to resized files
  817. .<PAPERSIZE>.SCALED.pdf is added in mixed mode
  818. Page Detection Modes:
  819. a, adaptive Default mode, tries all the methods below
  820. c, cat+grep Forces the use of the cat + grep method
  821. m, mdls Forces the use of MacOS Quartz mdls
  822. p, pdfinfo Forces the use of PDFInfo
  823. i, identify Forces the use of ImageMagick's Identify
  824. Valid Ghostscript Paper Names:
  825. $paperList
  826. Notes:
  827. - Adaptive Page size detection will try different modes until
  828. it gets a page size. You can force a mode with -m 'mode'.
  829. - Options must be passed before the file names to be parsed.
  830. - Having the extension .pdf on the output file name is optional,
  831. it will be added if not present.
  832. - File and folder names with spaces should be quoted or escaped.
  833. - The scaling is centered and using a scale bigger than 1 may
  834. result on cropping parts of the pdf.
  835. Examples:
  836. $PDFSCALE_NAME myPdfFile.pdf
  837. $PDFSCALE_NAME myPdfFile.pdf myScaledPdf
  838. $PDFSCALE_NAME -v -v myPdfFile.pdf
  839. $PDFSCALE_NAME -s 0.85 myPdfFile.pdf myScaledPdf.pdf
  840. $PDFSCALE_NAME -m pdfinfo -s 0.80 -v myPdfFile.pdf
  841. $PDFSCALE_NAME -v -v -m i -s 0.7 myPdfFile.pdf
  842. $PDFSCALE_NAME -h
  843. "
  844. }
  845. ######### START EXECUTION
  846. getOptions "${@}"
  847. main
  848. exit $?