summaryrefslogtreecommitdiff
path: root/pdf2dxf.pl
blob: 430a134151e49642bc718f3f124bb2db4f55dd91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/perl

# Convert a PDF/PS/EPS file to DXF
# All color and style information is dropped.

## Copyright (c) 2020 by Thomas Kremer
## License: GPL ver. 2 or 3

# This script requires CAM::PDF and ghostscript.

use strict;
use warnings;
use File::Temp;
use CAM::PDF;
use DXF;

# Ghostscript does all text work for us.
sub do_gs {
  my ($infile,$outfile) = @_;
  my @cmd = (qw(gs -q -dBATCH -dSAFER -dNOPAUSE -sDEVICE=pdfwrite
               -dCompressPages=false -dNoOutputFonts -dCompressStreams=false
               -dUNROLLFORMS),"-sOutputFile=$outfile",$infile);
  my $res = system(@cmd);
  die "gs returned an error" unless $res == 0;
}

# I do not recommend CAM::PDF. That code really stinks of eval.
# But the alternatives are just impotent.
{
  package CAM::PDF::Renderer::DXF;
  use base "CAM::PDF::GS";
  BEGIN {
    sub handler {
      my $name = shift;
      return eval q!sub {
        my $self = shift;
        #my ($x,$y) = $self->userToDevice(@{$self->{last}});
        #print STDERR "$name($x,$y): ".join(",",map $_//"undef", @_)."\n";
        $self->!."SUPER::$name".q!(@_);
        $self->do_push($name,@_);
      }!;
    }
    no strict "refs";
    *$_ = handler($_) for qw(l h v y c re); #qw(w d m l s c);
  }
  # # We want to catch all pdf commands, so we can notice the missing ones:
  # sub AUTOLOAD {
  #   my $sub = handler($AUTOLOAD);
  #   *$AUTOLOAD = $sub;
  #   goto &$sub;
  # }
  # sub can {
  #   return 1;
  # }
  my %ignored_commands;
  $ignored_commands{$_} = 1 for qw(
    i j J ri Tc TL Tr Ts Tw w
    g G rg RG k K cm d m
    S s F f fstar B Bstar b bstar n
    renderText TJ Tj quote doublequote
    BT Tf Tstar Tz Td TD Tm
    gs
  );
  sub do_push {
    my ($self,$name,@args) = @_;
    my @p = $self->userToDevice(@{$self->{last}});
    my @p1 = $self->userToDevice(@{$self->{current}});
    my @entities = ();
    if ($name eq "l" || $name eq "h") { #lineto, closepath
      @entities = (
        DXF::lol(LINE => { x=>$p[0],y=>$p[1],x1=>$p1[0],y1=>$p1[1] })
      );
    } elsif ($name =~ /^[cvy]$/) { # cubics...
      my @q1 = $self->userToDevice(@args[0,1]);
      my @q2 = @q1;
      if ($name eq "c") {
        @q2 = $self->userToDevice(@args[2,3]);
      } elsif ($name eq "v") { # yes, that is how they are specified. m-(
        @q1 = @p;
      } else {
        @q2 = @p1;
      }
      #$q1[$_] = $q1[$_]*2/3+$p[$_]*1/3 for 0..$#q1;
      #$q2[$_] = $q2[$_]*2/3+$p1[$_]*1/3 for 0..$#q2;
      my @points = (\@p,\@q1,\@q2,\@p1);
      my @x = map $_->[0], @points;
      my @y = map $_->[1], @points;
      @entities = (
        DXF::lol(SPLINE => { x=>\@x,y=>\@y,int1=>3,int=>8 }) # open,planar
      );
    } elsif ($name eq "re") { #rectangle
      my @points = (map [@args[0,1]],0..3);
      $_->[0] += $args[2] for @points[1,2];
      $_->[1] += $args[3] for @points[2,3];
      $_ = [$self->userToDevice(@$_)] for @points;
      my @x = map $_->[0], @points;
      my @y = map $_->[1], @points;
      @entities = (
        DXF::lol(LWPOLYLINE => {x => \@x, y => \@y, int => 1})
      );
    #} elsif ($name eq "wdmlsc") {
    } else {
      die "unsupported command \"$name\"" unless $ignored_commands{$name};
    }
    push @{$self->{refs}{dxf}}, @entities if @entities;
  }

  sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);
    $self->{refs}{dxf} = [];
    return $self;
  }

  sub get_dxf {
    my $self = shift;
    my $dxf = File::DXF->new;
    $dxf->add_entities($self->{refs}{dxf});
    return $dxf;
  }
}

my $tempfile = File::Temp->new;
do_gs($ARGV[0],$tempfile->filename);
my $pdf = CAM::PDF->new($tempfile->filename);
my $gs = $pdf->getPageContentTree(1)->traverse("CAM::PDF::Renderer::DXF");
my $dxf = $gs->get_dxf;
print $dxf->to_dxf;