30

Is there a (freely available) tool that will check the conformance of a PDF file to the PDF standard?

I've heard this referred to as "pre-flight" or something along those lines.

5
  • 7
    This looks borderline for 'on topic'. It's not really directly related to TeX, although I can see why a free pre-flight tool would be useful for TeX-based book production.
    – Joseph Wright
    Nov 1, 2011 at 7:55
  • Thought so this myself... should I close / move this question somewhere else?
    – mpr
    Nov 1, 2011 at 10:57
  • 7
    I think it's relevant, because when inserting PDF figures (I use pdfLaTeX) if they don't follow certain standard they don't work (when I make annotations in Acrobat I must use their Preflight tool to "flatten" them for pdfLaTeX to see) so I'd be interested in a free tool capable of checking if a PDF is fully suitable for pdfLaTeX, for example.
    – JorgeGT
    Nov 1, 2011 at 17:05
  • @JorgeGT That's a somewhat different question, and is usually best handled by using GhostScript to rewrite the PDF file.
    – Joseph Wright
    Nov 1, 2011 at 19:05
  • 1
    @JosephWright: I used to use gs to rewrite my pdf output, and although it fixed some problems, it created others. I wouldn't recommend it in general.
    – user6853
    Jan 25, 2013 at 2:12

4 Answers 4

11

Multivalent has a free (as in beer) pdf validation tool. It's definitely not as powerful as Acrobat preflight, but it may be enough for you.

Preflight usually covers more than simply verifying conformance to the pdf format. It also encompasses making sure fonts are embedded, colour spaces are correct, image resolutions are appropriate, etc. Maybe you don't need this type of thing.

3
  • 1
    I browsed Multivalent's page, downloaded the Multivalent20091027.jar file and yet couldn't make the validator work... :S
    – mpr
    Nov 2, 2011 at 12:15
  • 2
    Here's general instructions on how to use multivalent commandline tools: multivalent.sourceforge.net/Tools/HowToRun.html I can't be more specific because "couldn't make the validator work" doesn't give much clue about the problem....
    – Lev Bishop
    Nov 2, 2011 at 13:37
  • Well, it tells you to run tool.pdf.Validate but there's no package called tool in the download at all.
    – mjaggard
    Sep 23, 2013 at 14:59
9

Try these:

  • qpdf is a free tool that does some checks on PDFs, but these are mostly syntax checks.
  • Adobe Acrobat's preflight tool. But that's not free.
2
  • Qdf indeed seems interesting, but I was really looking for something more along the lines of Adobe's preflight tool, I don't own a copy of Adobe's suit but would love a free alternative to it (preflight, that is).
    – mpr
    Nov 2, 2011 at 4:05
  • As far as I can tell, qpdf doesn't really do much besides checking for syntactically invalid pdfs, which in my experience are usually not the problem. Usually rip issues seem to be related to fonts or transparency.
    – user6853
    Jan 25, 2013 at 2:14
4

There are lots of websites offering to validate your pdf. Please google for pdf, validate, online, and you get adresses like http://www.pdf-tools.com/pdf/validate-pdfa-online.aspx

Disadvantage is that somebody else anywhere may use your pdf to an end you do not like. To me being a lawyer a strict no-go.

1
  • Yes, but useful if the document shall be published in the internet anyway.
    – Stephen
    Nov 2, 2011 at 18:53
3

As JorgeGT has pointed out, the pdf output by pdflatex can be buggy not because of a bug in pdflatex but because of problems with the figures you include. I thought the following perl script might be helpful to others. It runs on linux. It's meant to give a preflight check to a figure that is going to be included in a LaTeX document, and it's designed to catch things that have been problematic for me in the past. I normally have an Inkscape .svg file sitting in the same directory with a rendering of that file into .pdf, .jpg, or .png, and the script is written assuming that organization.

When I have an Inkscape file that uses transparency, I find that it's safest to eliminate the transparency before sending the pdf to be ripped. Although it may work, the hardcopy sometimes comes out garbled. The simplest thing to do is to use Inkscape to render the figure as a png file, then imagemagick to remove the transparency from the png.

I've had problems in the past when pdf figures had fonts embedded in them and I didn't know it. It shouldn't matter in theory, but in practice it caused rip problems. You could also have legal issues if you don't have permission to redistribute fonts that you didn't realize were embedded in a pdf. To get rid of this issue with Inkscape figures, you can do "convert text to paths" when you save as pdf.

#!/usr/bin/perl

use strict;

# usage:
#   preflight_one_fig.pl foo.svg
# Checks whether there is no rendered version of foo.svg.
# Checks whether it was rendered to foo.png. If so, complains if it has transparency.
# Checks whether there is also a foo.pdf. If there is, checks foo.svg and foo.pdf for problems:
#   transparency (checked for in the svg)
#   fonts embedded in pdf
#   bad pdf structure
#   pdf older than svg
# If there's a problem, prints a message to stdout and exits with nonzero error code.

# requires the following tools:
#   xml_grep (part of ubuntu package xml-twig-tools)
#   qpdf (ubuntu package qpdf)
#   pdffonts (ubuntu package poppler-utils)
#   identify and mogrify (ubuntu package imagemagick)

my $svg = $ARGV[0];

my $pdf = $svg;
$pdf =~ s/\.svg$/.pdf/;
if (-e $pdf) {
  my $err = check_pdf($svg,$pdf);
  if ($err) {err($err)}
  exit(0);
}
else {
  # There's no pdf, so there'd better be a .jpg or .png
  foreach my $e('jpg','png') {
    my $bitmap = $svg;
    $bitmap =~ s/\.svg$/.$e/;
    if (-e $bitmap) {
      if ($e eq 'png') {
        my $f = `identify -format '%[channels]' $bitmap`;
        if ($f=~/rgba/) {
          my $c = "mogrify -background white -flatten -alpha off $bitmap";
          # system($c);
          err("file $bitmap contains transparency; fix with\n  $c\nand then check visually");
        }
      }
      exit(0);
    }
  }
  err("file $svg does not exist as .pdf, .jpg, or .png");
}

sub err {
  my $message = shift;
  print $message,"\n";
  exit(-1);
}

sub check_pdf {
  my ($svg,$pdf) = @_;
  my $err = check_for_stale_pdf($svg,$pdf);
  return $err if $err;
  my $err = check_pdf_for_fonts($svg,$pdf);
  return $err if $err;
  my $err = check_pdf_for_transparency($svg,$pdf);
  return $err if $err;
  my $err = check_pdf_for_structure($svg,$pdf);
  return $err if $err;
  return undef;
}

sub check_for_stale_pdf {
  my ($svg,$pdf) = @_;
  # -M is relative age of file in days, floating point
  (-M $svg) > (-M $pdf) or return 
         "file $pdf is older than file $svg, ".(-M $svg)." < ".(-M $pdf);
  return undef;
}

sub check_pdf_for_structure {
  my ($svg,$pdf) = @_;
  system("qpdf --check $pdf 1>/dev/null 2>/dev/null")==0 or return "bad structure for $pdf detected by qpdf --check:\n";
  return undef;
}

sub check_pdf_for_fonts {
  my ($svg,$pdf) = @_;
  my $fonts = `pdffonts $pdf`;
  $fonts =~ /\A.*\n.*\n(.*)/; # strip header lines
  my $f = $1;
  if ($f ne '') {return "embedded fonts found in file $pdf, made from $svg"}
  return undef;
}

sub check_pdf_for_transparency {
  my ($svg,$pdf) = @_;

  # for efficiency, first do a rough check:
  return undef unless `grep -e "opacity:[^1]" $svg`;

  # Now do a more reliable check.
  # There are three types of opacity: fill-opacity, stroke-opacity, and opacity (applied to whole groups).
  my $transp = `xml_grep --cond='*[\@style]' $svg  | grep -e "opacity:[^1]"`;
  if ($transp ne '') {
    # Often we get something like this:
    #     style="fill:none;fill-opacity:0.75"
    # This is harmless because there is no fill, so the transparency of the fill is irrelevant.
    while ($transp=~/style\s*=\s*"([^"]*)"/gi) {
      my $style = $1;
      my %styles = ();
      foreach my $item(split /;/,$style) {
        if ($item=~/(.*):(.*)/) {$styles{lc($1)} = lc($2)}
      }
      if (defined $styles{'opacity'} && $styles{"opacity"}<1) {
        return report_transparency($svg,$style);
      }
      foreach my $sf('stroke','fill') {
        if (   $styles{$sf} ne 'none' # works correctly if undef
            && defined $styles{"${sf}-opacity"}
            && $styles{"${sf}-opacity"}<1 ) { 
          return report_transparency($svg,$style);
        }
      }
    }
  }
  return undef;
}

sub report_transparency {
  my ($svg,$style) = @_;
  $style =~ /(.{0,22}opacity:[^1].{0,10})/;
  my $shorter = $1;
  return "transparency found in file $svg : ...$shorter...";
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .