9

I'm looking to convert a .jpg into a .pdf, so that the .jpg is positioned at a certain location: centered on (say) an A4 page, with a minimum border of 30 pixels all around between image and page borders.

My starting point is just this (it just creates a PDF the same size as the JPG):

convert image.jpg image.pdf
tanius
  • 874

4 Answers4

11

I assume you want to add a border of, say 30px, on all four sides. Use the -border option for the size and -bordercolor for the colour:

convert -border 30 -bordercolor white input.png output.pdf

More information can be found here: Imagemagick - Adding/Removing Image Edges

If you want the final PDF to be of a particular size, you can use the following options:

convert \
  -page A4 \
  -gravity northwest \
  -border 30 \
  -bordercolor white \
  input.png output.pdf
Marco
  • 33,548
  • thanks, ya that puts it down 30pixels which i want, also looking to make the pdf a whole page, with just the image 30 pixels down at the top – chris mccoy Jul 25 '12 at 18:12
  • I updated my answer, but it's hard to tell what you actually want. If it's still not what you are looking for, it might make sense to update your question, rephrase it, add more details or an image of what you want to achieve. – Marco Jul 25 '12 at 18:54
7

Here is a little script that does what you want, and can do it to multiple .jpg files at the same time, converting each into its own one-page .pdf file.

Save the script as imgs_to_pdfs.sh and call it with one or multiple filename arguments, like this:

./imgs_to_pdfs.sh myimg1.jpg myimg2.jpg img-*.jpg

Output filenames will correspond to input filenames with .jpg replaced with .pdf. So make sure the script will not accidentally overwrite existing files!

How it works

  • For the page to mount the image on, the script uses A4 format. It calculates the A4 dimensions itself, since the A4 keyword is seemingly not supported in recent ImageMagick versions anymore.
  • The image is not resampled ("scaled") by the script, just shown centered with a certain resolution on the A4 PDF page canvas. So, no image information is lost by scaling down, and no useless file size added by scaling up.
  • Instead of a 30 pixel minimum margin all around, the script leaves a space between image and PDF page borders. The advantage of this over adding a white border to the image is that it does not add to the file size, and that you can extract the unmodified image later from the PDF if desired, using a command like pdfimages -j file.pdf img.
  • By default, the border around the image will be set to ≥5% of each page dimension. The image is scaled proportionally to achieve this, so x dimension borders will be 5% and y dimension borders will be lager, or the other way round, depending on image proportions. You can adapt the size of the borders by adapting the resolution factor in the script. Currently it is 1.1, leading to 110% of the resolution for an A4 page fit. So the image covers only 90% of the A4 page dimensions, leaving two 5% borders. If you set the factor to 1.2, you get two 10% borders and so on.

Other details

  • Here's the proof how the formulas in the script lead to 5% borders:
    1. Page size in pixels is calculated as page_size_x = density * 8.27
    2. Density is calculated as density = img_size_x / 8.27 * 1.1. (This assumes the case where the x dimension needs the higher density to leave a 5% border empty.)
    3. Line 2 in line 1 yields: page_size_x = (img_size_x/8.27*1.1) * 8.27 = img_size_x * 1.1. So indeed, the page is 110% the pixel width of the image, giving two 5% borders.
  • Some people seem to need a -repage operation (like here) to prevent page size from being slightly "off". Did not need it, but if you do, try -repage ${page_size_x}x${page_size_y} or -repage A4 as last operation in the convert call.

Script source code

#!/bin/bash
# Converts input images to one-page PDF files each, without changing image data.
# The image is centered on a A4 page with a 5% border.

# bc function to calculate maximum of two floats
bc_functions="
define max(a,b) {
  if (a>b) {
    return(a)
  } else {
   return(b)
  }
} ";

for file in "$@"; do \
  # Determine image dimensions in pixels.
  img_size_x=$(identify -format "%w" "$file");
  img_size_y=$(identify -format "%h" "$file");

  # Calculate image density (in dpi) needed to fit the image and a 5% 
  # border all around on an A4 page (8.27x11.69"). Factor 1.1 creates 
  # 2*5% borders, see https://unix.stackexchange.com/a/220114 for details.
  min_density_x=$(echo "$img_size_x/8.27*1.1"  | bc -l);
  min_density_y=$(echo "$img_size_y/11.69*1.1" | bc -l);
  # Use the higher density to prevent any dimension exceeding the required fit.
  density=$(echo "$bc_functions max($min_density_x,$min_density_y)" | bc -l);

  # Calculate canvas dimensions in pixels.
  # (Canvas is an A4 page (8.27x11.69") with the calculated density.)
  page_size_x=$(echo  "8.27*$density" | bc -l);
  page_size_y=$(echo "11.69*$density" | bc -l);

  # Center image on a larger canvas (with a size given by "-extent").
  convert "$file" \
    -gravity center -extent ${page_size_x}x${page_size_y} \
    -units PixelsPerInch -density $density \
    -format pdf -compress jpeg \
    "${file/.jpg/.pdf}";
done;

References

tanius
  • 874
  • To do: Make the script refuse to run if any output .pdf files exist, to prevent accidentally overwriting user data. – tanius Feb 17 '16 at 12:18
2

What I finally went with to center the image on the page with a border was to specify the geometry both for the page and for the resize and extents. The sizes to resize and extent are reduced by 2x the border size in both dimensions.

Dimensions for the pages sizes are listed at: http://www.imagemagick.org/script/command-line-options.php#page

For Letter (612x792):

convert -page Letter -resize 552x732\> -extent 552x732 -background white -gravity Center -border 30 -bordercolor white image.jpg image.pdf

For A4 (595x842):

convert -page A4 -resize 535x782\> -extent 535x782 -background white -gravity Center -border 30 -bordercolor white image.jpg image.pdf
0

The answer in https://unix.stackexchange.com/a/220114 uses the -extent option which modifies the source image by adding white spaces to the borders.

This script uses the -page option with an offset to resize the canvas and position the image, which doesn't modify the images. See script on Github and copied here.

#!/bin/bash
# Converts input images to one-page PDF files each, without changing image data.
# The image is centered on a A4 page with a 5% border.
# Adapted from https://unix.stackexchange.com/a/220114
#
# Usage: [command] image1.jpg image2.png ...
# Output: PDF files named after the images e.g. image1.pdf


# bc function to calculate maximum of two floats
bc_functions="
define max(a,b) {
  if (a>b) {
    return(a)
  } else {
    return(b)
  }
};";

# Do the calculation in string $1 and echo the result.
function calc {
  # Define bc functions so we can use it for the calc.
  echo "$bc_functions $1" | bc -l;
}


for file in "$@"; do \
  # Determine image dimensions in pixels.
  img_size_x=$(identify -format "%w" "$file");
  img_size_y=$(identify -format "%h" "$file");

  # Calculate image density (in dpi) needed to fit the image and a 5% 
  # border all around on an A4 page (8.27x11.69"). Factor 1.1 creates 
  # 2*5% borders, see https://unix.stackexchange.com/a/220114 for details.
  min_density_x=$(calc "$img_size_x / 8.27 * 1.1");
  min_density_y=$(calc "$img_size_y / 11.69 * 1.1");

  # Use the higher density to prevent any dimension exceeding the required fit.
  density=$(calc "max($min_density_x,$min_density_y)");

  # Calculate canvas dimensions in pixels.
  # (Canvas is an A4 page (8.27x11.69") with the calculated density.)
  page_size_x=$(calc "8.27 * $density");
  page_size_y=$(calc "11.69 * $density");

  offset_x=$(calc "($page_size_x - $img_size_x) / 2 * 72 / $density");
  offset_y=$(calc "($page_size_y - $img_size_y) / 2 * 72 / $density");

  # Center image on a larger canvas.
  convert "$file" \
    -page ${page_size_x}x${page_size_y}+${offset_x}+${offset_y} \
    -units PixelsPerInch -density $density \
    -format pdf -compress jpeg \
    "${file/.jpg/.pdf}";
done;

Here's a more detailed article explaining how the script works, and its usage.