convert images to pdf: How to make PDF Pages same size

85,713

Solution 1

Last time I used convert for such a task I explicitly specified the size of the destination via resizing:

$ i=150; convert a.png b.png -compress jpeg -quality 70 \
      -density ${i}x${i} -units PixelsPerInch \
      -resize $((i*827/100))x$((i*1169/100)) \
      -repage $((i*827/100))x$((i*1169/100)) multipage.pdf

The convert command doesn't always use DPI as default density/page format unit, thus we explicitly specify DPI with the -units option (otherwise you may get different results with different versions/input format combinations). The new size (specified via -resize) is the dimension of a DIN A4 page in pixels. The resize argument specifies the maximal page size. What resolution and quality to pick exactly depends on the use case - I selected 150 DPI and average quality to save some space while it doesn't look too bad when printed on paper.

Note that convert by default does not change the aspect ratio with the resize operation:

Resize will fit the image into the requested size. It does NOT fill, the requested box size.

(ImageMagick manual)

Depending on the ImageMagick version and the involved input formats it might be ok to omit the -repage option. But sometimes it is required and without that option the PDF header might contain too small dimensions. In any case, the -repage shouldn't hurt.

The computations use integer arithmetic since bash only supports that. With zsh the expressions can be simplified - i.e. replaced with $((i*8.27))x$((i*11.69)).

Lineart Images

If the PNG files are bi-level (black & white a.k.a lineart) images then the img2pdf tool yields superior results over ImageMagick convert. That means img2pdf is faster and yields smaller PDFs.

Example:

$ img2pdf -o multipage.pdf a.png b.png

or:

$ img2pdf --pagesize A4 -o multipage.pdf a.png b.png

Solution 2

What you really want to use is:

$ convert a.png b.png -compress jpeg -resize 1240x1753 \
                      -extent 1240x1753 -gravity center \
                      -units PixelsPerInch -density 150x150 multipage.pdf

-extent actually extends the image to be 1240x1753, while -resize keeps the image's ratio, fitting it into either 1240x... or ...x1753.

The -gravity parameter is optional but can be used to center the image when extending.

Solution 3

Addition to caugner's answer:

having installed IM v6.6.9-7 i found out the -gravity parameter needs to be placed in between -resize and -extent to have an effect.

additionally (altough not part of the o.p. question) i found setting a different background-color appealing which would result in the total command of

convert in.jpg -resize 1240x1750 -background black -compose Copy\
               -gravity center -extent 1240x1750\
               -units PixelsPerInch -density 150 out.pdf

another useful variation i often use when i don't want to re-scale an image that already comes in the correct aspect-ratio but keep its individual resolution is

convert in.jpg -units PixelsPerInch -set density '%[fx:w/8.27]'\
               -repage a4 out.pdf

where the target density is dynamically determined by calculating the width divided by 8.27 (which is the width in inch of an A4 page). the -repage a4 parameter can be omitted most of the time but i've had a few cases where the resulting .pdf would have a different format sligtly off the A4 dimensions of 210x297mm (8.27x11.6")

Solution 4

I highly recommend the Python CLI program img2pdf for lossless conversion:

https://gitlab.mister-muffin.de/josch/img2pdf

Example usage:

img2pdf img1.png img2.png -o out.pdf

Solution 5

I find the following script convenient which combines the answers listed here as well as some problems I had with the floating point calculation:

endInputArgs=$(($#-1))

quoted_args="$(printf " %q" "${@:1:$endInputArgs}")"
output_arg="$(printf " %q" "${@:$#:1}")"

ratiox=$(echo "150*8.27" | bc -l)
ratioy=$(echo "150*11.69" | bc -l)

bash -c "convert $quoted_args -compress jpeg -resize 1240x1753 \
  -units PixelsPerInch -density 150x150 -repage ${ratiox}x${ratioy} $output_arg"

The script is called (saved as a file images2pdf)

images2pdf file\ 1.jpg file\ 2.jpg file\ 3.jpg output.pdf

/edit: Added "-l" flag according to comment by tanius for better precision.

Share:
85,713

Related videos on Youtube

Jiew Meng
Author by

Jiew Meng

Updated on September 18, 2022

Comments

  • Jiew Meng
    Jiew Meng over 1 year

    I did something like

    convert -page A4 -compress A4 *.png CH00.pdf
    

    But the 1st page is much larger than the subsequent pages. This happens even though the image dimensions are similar. These images are scanned & cropped thus may have slight differences in dimensions

    I thought -page A4 should fix the size of the pages?

  • brownian
    brownian about 11 years
    Thank you! -extent is really what I want to use :) -- please, add missed left quote in front of -extent, thanks!
  • caugner
    caugner almost 11 years
    Thank you, I have eventually added the missing tick! ;-)
  • Scolytus
    Scolytus about 10 years
    when using -repage a4 I get a invalid argument for option '-repage': a4
  • maxschlepzig
    maxschlepzig about 10 years
    @Scolytus, on a Fedora 19 system I've observed a similar issue - it seems that -repage does not support the a4 name anymore. I've worked around this via shell arithmetic: -repage $((150*8.27))x$((150*11.69))
  • Michael Scheper
    Michael Scheper about 9 years
    I assume those magic numbers are 150dpi, and A4 expressed in legacy units?
  • maxschlepzig
    maxschlepzig about 9 years
    @MichaelScheper, yes, dpi and inches.
  • dma_k
    dma_k about 9 years
    Thanks, help me. Actually -density 150 argument was important to add .
  • Alen Milakovic
    Alen Milakovic almost 9 years
    I was only able to get this to work by leaving off -density 150x150 (I was trying to convert a JPG). Why is it necessary to include the density, and how do you know what it is, anyway?
  • tanius
    tanius almost 9 years
    General hint: $(echo "150*8.27" | bc) is still not great for floating point. Works here because it's a multiplication. For $(echo "150/8.27" | bc) though, the result is 18 (truncated to integer). Instead, call bc with higher scale: $(echo "150/8.27" | bc -l), the results is 18.137847….
  • Yotam
    Yotam almost 9 years
    In the above, I had to change -set density $ImgDens into -density $ImgDens
  • maxschlepzig
    maxschlepzig almost 8 years
    @FaheemMitha, the resize command in the above examples assumes a resulting resolution of 150 DPI such that we arrive at DIN A4 dimensions - the density option explicitly sets that attribute for the PDF format. Otherwise ImageMagick ends up writing the wrong dimensions in the PDF - at least the Fedora 23 version I currently use in combination with PNG/PBM files does so.
  • Alen Milakovic
    Alen Milakovic almost 8 years
    Hi @maxschlepzig, thanks for the clarification. What made you reply after nearly a year? :-)
  • Marcus Junius Brutus
    Marcus Junius Brutus over 7 years
    Using the incantation as provided gave me bash: i*8.27: syntax error: invalid arithmetic operator (error token is ".27") in GNU bash 4.3.11. I had to use the following form instead: ` -resize $(echo ${i}*8.27 | bc)x$(echo ${i}*11.69 | bc). BTW, the -repage` was also necessary.
  • maxschlepzig
    maxschlepzig over 7 years
    @MarcusJuniusBrutus, I tested the last revision just in zsh and apparently bash doesn't support decimal arithmetic. As an alternative, you can also use intetger only arithmetic like this echo $((i*827/100))x$((i*1169/100)). This saves the bc calls. I'll update my answer.