15

I have myfile.ps with a vector image included. But when I run

ps2pdf myfile.ps

it seems that the output page size is A4: the vector image is too large and become cut away, so about one inch is lost.

The following pseudo-header is printed in the output PDF file, in addition to the original vector image:

PLOT SIZE:8.02x8.62Inches
Magnification:7354.21X

Is there any option or any way to convert the PS file to a PDF preserving the original paper size?

BowPark
  • 1,340
  • 2
  • 21
  • 31

4 Answers4

20

If the input postscript has an EPS BoundingBox, this should preserve the page size:

ps2pdf -dEPSCrop <input.ps> <output.pdf>
Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
Raman
  • 17,606
  • 5
  • 95
  • 112
  • @roy What version of `ps2pdf` / `ghostscript`? What was the error message? Does your input postscript contain a bounding box? You appear to have downvoted but you don't provide a lot of information. – Raman Jun 05 '17 at 15:41
  • No error shown but the resulting PDF does not preserve the page size. As you say, maybe that option reads the bounding box, but this question is about the page size, which is stored in the DocumentMedia header, see my answer below. – roy Jun 07 '17 at 21:51
  • @roy Thanks for the clarification. I updated my answer to reflect its limitations. – Raman Jun 08 '17 at 00:35
  • 1
    FWIW, I had the same issue running `ps2pdf` in macOS in 2023 and the `-dEPSCrop` option worked like a charm! Thank you so much. – Giacomo1968 Jan 30 '23 at 04:53
9

I doubt your quoted 2 lines are really inside the PS file as quoted... Aren't they preceeded by % comment characters?

  • If they weren't preceeded by such characters, no PS interpreter would work, because they are no known PostScript operators.

  • If they are preceeded by such characters, the PS interpreter would simply ignore them, because... they are comments only! :-)

If you want to convert this PS file to PDF, it is better to run Ghostscript directly (ps2pdf is only a thin shell script wrapper around a Ghostscript command anyway):

gs -o myfile.pdf     \
   -sDEVICE=pdfwrite \
   -g5775x6207       \
   -dPDFFitPage      \
    myfile.ps

Explanation:

  1. -g... gives the medium size in pixels.
  2. An A4 page has a dimension of 595x842pt (PostScript points).
  3. 1 Inch is the same as 72 PostScript points.
  4. Ghostscript internally by default computes with a resolution of 720 pixels per inch when it comes to PDF output.
  5. Hence for PDF output 595x842pt == 5950x8420px.
  6. Hence for your case in question 8.02x8.62Inches ≈≈ 5775x6207px.
Kurt Pfeifle
  • 86,724
  • 23
  • 248
  • 345
  • You were right: I mixed up the output PDF header with the PS header, wrongly supposing that they were the same thing. Now I corrected the question. With your values, unfortunately, the image is still cut away, maybe because that "header" was not so trustworthy. But increasing the output size by some hundred pixels it gives the correct result. Thank you! – BowPark May 11 '15 at 09:30
4

I am not allowed to comment, but I must warn everyone that all the current answers are vulnerable to malicious postscript files.

Using gs like this is VERY dangerous. ps2pdf internally uses the -dSAFER option which would, for example, prevent an untrusted postscript file from encrypting your files and rendering a pdf that demands a ransom payment from you for the decryption key! ALWAYS use -dSAFER!

While -o outputFile.pdf is nice, it is also undocumented (via man page or gs -h) as of version 9.23.

The below command works without worrying about the top being cut off like with the other solutions:

gs -sOutputFile=file.pdf -dNOPAUSE -dBATCH -sPAPERSIZE=a4 -sDEVICE=pdfwrite -dSAFER file.ps

-sPAPERSIZE=a4 is how the a4 paper size is specified.

to get the page size you can look for a line like the following:

%%PageBoundingBox:·12·12·583·830

and then use

gs -sOutputFile=file.pdf -dNOPAUSE -dBATCH -g583x830 -r72 -sDEVICE=pdfwrite -dSAFER file.ps

and it works perfectly.

0

Based on @Kurt Pfeifle's answer I wrote this Perl script to do the task:

#! /usr/bin/env perl
use strict;
use warnings;

use Scalar::Util qw(looks_like_number);
use List::Util qw(all);


sub ps2pdf;
sub get_ps_headers;
sub get_media_size;
sub main;

# Run the program
main();


# Function: main
#
# Program's entry point.
#
sub main {
   for (@ARGV) {

      # check input file
      if(not -r) {
         print "WARN: Cannot read input file: $_\n";
         next;
      }

      # build PDF file name
      my $pdf = $_;
      $pdf =~ s/(\.e?ps)?$/.pdf/i;

      ps2pdf($_, $pdf);
   }
}


# Function: ps2pdf
#
# Converts a PostScript file to PDF format using GhostScript,
# keeping the medium size.
#
# Params:
#
#     $ps_file  - (string) Input [E]PS file name
#     $pdf_file - (string) Output PDF file name
#
sub ps2pdf {
   my ($ps_file, $pdf_file) = @_;
   my $cmd = "gs -q -sDEVICE=pdfwrite -dPDFFitPage ";

   # try to find the media size
   my ($width, $height) = get_media_size(get_ps_header($ps_file));

   # keep media size
   if(defined $height) {
      $cmd .= "-g${width}x${height} ";
   }

   # set input/output
   $cmd .= "-o $pdf_file $ps_file";

   print "Running: $cmd\n";

   system($cmd);
}


# Function: get_media_size
#
# Computes the size of a PostScript document in pixels,
# from the headers in the PS file.
#
# Params:
#
#     $hdr  - (hash ref) Parsed PS header values
#
# Returns:
#
#     On success: Two-element array holding the document's width and height
#     On failure: undef
#
sub get_media_size {
   my ($hdr) = @_;

   # we need the DocumentMedia header
   return undef if not defined $hdr->{DocumentMedia};

   # look for valid values
   my @values = split(/\s+/, $hdr->{DocumentMedia});
   return undef if scalar @values < 3;
   my ($width, $height) = @values[1, 2];

   return undef if not all { looks_like_number($_) } ($width, $height);

   # Ghostscript uses a default resolution of 720 pixels/inch,
   # there are 72 PostScript points/inch.
   return ($width*10, $height*10);
}


# Function: get_ps_header
#
#  Parses a PostScript file looking for headers.
#
# Params:
#
#     $ps_file - (string) Path of the input file
#
# Returns:
#
#     (hash ref) - As expected, keys are header names,
#     values are corresponding header values. A special key
#     named `version' is included for headers of the type
#     `PS-Adobe-3.0'
#
sub get_ps_header {
   my ($ps_file) = @_;
   my %head;

   open my $fh, "<$ps_file" or die "Failed to open $ps_file\n";
   while(<$fh>) {
      # look for end of header
      last if /^%%EndComments\b/;

      # look for PS version
      if(/^%!(\w+)/) {
         $head{version} = $1;
      }

      # look for any other field
      # Ex: %%BoundingBox: 0 0 1008 612
      elsif(/^%%(\w+)\s*:\s*(.*\S)/) {
         $head{$1} = $2;
      }

      # discard regular comments and blank lines
      elsif(/^\s*(%.*)?$/) {
         next;
      }

      # any other thing will finish the header
      else {
         last;
      }
   }

   return \%head;
}
roy
  • 463
  • 5
  • 10