#!/usr/local/bin/perl # a converter from magicpoint (mgp) to OpenOffice Impress (odp) # Copyright (C) 2008 Kengo Ichiki # $Id: mgp2odp,v 1.4 2008/02/16 04:47:52 ichiki Exp $ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use OpenOffice::OODoc; use Image::Size; ooLocalEncoding 'utf8'; # global variables # scale of 1024 pixcel to 27.94 cm, the longer side of letter size my $dpcm; # dot per cm $dpcm = 1024.0 / 27.94; #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $page : $page # $text : text of the item sub add_title { my ($p, $page, $text) = @_; my $draw_frame = $p->appendElement ($page, 'draw:frame', attribute => { 'presentation:style-name' => 'pr1', 'draw:text-style-name' => 'P1', 'draw:layer' => 'layout', 'svg:width' => '25.199cm', 'svg:height' => '3.507cm', 'svg:x' => '1.4cm', 'svg:y' => '0.837cm', 'presentation:class' => 'title' } ); # attach a new box to the frame my $text_box = $p->appendElement($draw_frame, 'draw:text-box'); # put a new paragraph in the box $p->appendParagraph(attachment => $text_box, text => $text, style => "P1"); 1; } #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $page : $page # $x, $y : (string) ex. $x = "1.4cm", $y = "4.914cm". # $width, $height : (string) ex. $width = "25.199cm", $height = "13.609cm". # $text : text of the item sub add_text { my ($p, $page, $x, $y, $width, $height, $text) = @_; my $draw_frame = $p->appendElement ($page, 'draw:frame', attribute => { 'presentation:style-name' => 'pr1', 'draw:text-style-name' => 'P1', 'draw:layer' => 'layout', 'svg:width' => $width, 'svg:height' => $height, 'svg:x' => $x, 'svg:y' => $y, 'presentation:class' => 'title' } ); # attach a new box to the frame my $text_box = $p->appendElement($draw_frame, 'draw:text-box'); # put a new paragraph in the box #$p->appendParagraph(attachment => $text_box, text => $text, style => "P5"); $p->appendParagraph(attachment => $text_box, text => $text, style => "P6"); 1; } #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $page : $page # $x, $y : (string) ex. $x = "1.4cm", $y = "4.914cm". # $width, $height : (string) ex. $width = "25.199cm", $height = "13.609cm". # OUTPUT # $text_box : (draw:text-box) : can be used by append_text() sub make_text_frame { my ($p, $page, $x, $y, $width, $height, $text) = @_; my $draw_frame = $p->appendElement ($page, 'draw:frame', attribute => { 'presentation:style-name' => 'pr3', 'draw:text-style-name' => 'P6', 'draw:layer' => 'layout', 'svg:width' => $width, 'svg:height' => $height, 'svg:x' => $x, 'svg:y' => $y, 'presentation:class' => 'title', 'presentation:user-transformed' => 'true' } ); # attach a new box to the frame my $text_box = $p->appendElement($draw_frame, 'draw:text-box'); $text_box; } #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $parent : # $text : text of the item (string) # $style_text : style-name such as 'T1', etc. sub append_text { my ($p, $parent, $text, $style_text) = @_; my $text_span = $p->appendElement ($parent, 'text:span', attribute => {'text:style-name' => $style_text}); # put a new paragraph in the box #$p->appendParagraph(attachment => $text_span, text => $text, style => "P6"); $p->OpenOffice::OODoc::XPath::setText($text_span, $text); 1; } #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $page : $page # $x, $y : (string) ex. $x = "1.4cm", $y = "4.914cm". # $width, $height : (string) ex. $width = "25.199cm", $height = "13.609cm". # OUTPUT # $text_box : draw:text-box sub add_outline { my ($p, $page, $x, $y, $width, $height) = @_; my $draw_frame = $p->appendElement ($page, 'draw:frame', attribute => { 'presentation:style-name' => 'pr2', 'draw:layer' => 'layout', 'svg:width' => $width, 'svg:height' => $height, 'svg:x' => $x, 'svg:y' => $y, 'presentation:class' => 'outline'}); # attach a new box to the frame my $text_box = $p->appendElement($draw_frame, 'draw:text-box'); $text_box; } #------------------------------------------------------------------------------ # INPUT # $p : $presentation # $tb : $text_box # $n1 : level of the list (1, 2, 3, ...) # $pstyle : paragraph style. if "" is given, "P" . ($n1+1) is used. # OUTPUT # (text:list-item) : can be used by append_text() sub make_list_item { my ($p, $tb, $n1, $ps) = @_; if ($n1 < 1){ $n1 = 0; } elsif ($n1 > 9) { $n1 = 9; } if ($ps eq '') { $ps = "P" . ($n1+1); } # $n is shifted as (0, 1, 2, ...) $n = $n1 - 1; my (@tli, @tl); # draw:text-box -> text:list $tl[0] = $p->appendElement($tb, 'text:list', attribute => {'text:style-name' => 'L2'}); for ($i = 0; $i < $n; $i ++) { $tli[$i] = $p->appendElement($tl[$i], 'text:list-item'); $tl [$i+1] = $p->appendElement($tli[$i], 'text:list'); } # draw:text-box -> text:list -> text:list-item $tli[$n] = $p->appendElement($tl[$n], 'text:list-item'); # put a new paragraph in the box my $text_p = $p->appendElement ($tli[$n], 'text:p', attribute => { 'text:style-name' => $ps } ); $text_p; } #------------------------------------------------------------------------------ # add image by appendElement # INPUT # $p : $presentation # $page : $page # $x, $y : (string) ex. $x = "1.4cm", $y = "4.914cm". # $width, $height : (string) ex. $width = "25.199cm", $height = "13.609cm". # $file : image file sub add_image { my ($p, $page, $x, $y, $width, $height, $file) = @_; my $draw_frame = $p->appendElement ($page, 'draw:frame', attribute => { 'draw:style-name' => 'gr1', 'draw:text-style-name' => 'P6', 'draw:layer' => 'layout', 'presentation:class' => 'graphic', 'svg:width' => $width, 'svg:height' => $height, 'svg:x' => $x, 'svg:y' => $y } ); my $draw_image = $p->appendElement ($draw_frame, 'draw:image', attribute => { 'xlink:type' => 'simple', 'xlink:show' => 'embed', 'xlink:actuate' => 'onLoad' } ); $p->appendParagraph (attachment => $draw_image, text => "", style => "P1"); # import the image my ($base, $path, $suffix) = File::Basename::fileparse($file, '\..*'); my $tmpl = $p->{'image_fpath'}; my $element = $p->getImageElement($draw_frame); my $link = $tmpl . $base . $suffix; $p->imageLink($element, $link); $p->raw_import($link, $file); 1; } #------------------------------------------------------------------------------ # style_text #------------------------------------------------------------------------------ # set @styles_text # INPUT # $p : $presentation # OUTPUT # @styles_text : array, $styles_text[$n] is for T$n style. sub init_styles_text { my $p = shift; my @styles_text; push @styles_text, ''; my @style; # T1 -- normal @style = $p->selectElementsByAttribute ('//style:style', 'style:name', 'T1'); unless (@style) { $t1 = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $t1); push @styles_text, 'normal default default'; } # T2 -- bold @style = $p->selectElementsByAttribute ('//style:style', 'style:name', 'T2'); unless (@style) { $t2 = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $t2); push @styles_text, 'bold default default'; } # T3 @style = $p->selectElementsByAttribute ('//style:style', 'style:name', 'T3'); unless (@style) { $t3 = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $t3); push @styles_text, 'italic default default'; } @styles_text; } #------------------------------------------------------------------------------ # get the index of text style # INPUT # @styles_text : # $style_text : string describing text style # OUTPUT # $i : if $i >= 0, $styles_text[$i] eq $style_text # if $i < 0, no entry exists in @styles_text. sub get_style_text { local (*styles_text, $style_text) = @_; my $n = @styles_text; for (my $i = 0; $i < $n; $i++) { if ($styles_text[$i] eq $style_text) { return $i; } } -1; } #------------------------------------------------------------------------------ # add text style # INPUT # @styles_text : # $p : $presentation # $flag_bold, $flag_italic : # $color, $bgcolor : if '' is given, 'default' is asigned in @styles_text. # OUTPUT # @styles_text : sub add_style_text { local (*styles_text, $p, $flag_bold, $flag_italic, $color, $bgcolor) = @_; if ($color eq '') { $color = "default"; } if ($bgcolor eq '') { $bgcolor = "default"; } my $label; if ($flag_bold == 0 and $flag_italic == 0) { $label = 'normal'; } elsif ($flag_bold == 1 and $flag_italic == 0) { $label = 'bold'; } elsif ($flag_bold == 0 and $flag_italic == 1) { $label = 'italic'; } elsif ($flag_bold == 1 and $flag_italic == 1) { $label = 'bold-italic'; } my $style_text = "$label $color $bgcolor"; my $n = get_style_text(\@styles_text, $style_text); if ($n < 0) { $n = @styles_text; # this is going to be the index for the new entry my $xml = '' . ' ' . ''; #print "# xml:\n" . $xml . "\n"; $p->appendElement('//office:automatic-styles', 0, $xml); push @styles_text, $style_text; } @styles_text; } #------------------------------------------------------------------------------ # style_paragraph #------------------------------------------------------------------------------ # set @styles_text # INPUT # $p : $presentation # OUTPUT # @styles_paragraph : array, $styles_paragraph[$n] is for P$n style. sub init_styles_paragraph { my $p = shift; my @styles_paragraph; push @styles_paragraph, ''; my @style; # P1 -- title my $xml = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $xml); push @styles_paragraph, 'default 0 0 0'; # for items at levels 1 to 9 (corresponding to P2 to P10, respectively) for (my $i=0; $i < 9; $i++) { my $left = ($i+1) * 1.2; my $ind; if ($i == 0) { $ind = -0.9; } elsif ($i == 1) { $ind = -0.8; } else { $ind = -0.6; } $xml = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $xml); push @styles_paragraph, 'default $left 0 $ind'; } @styles_paragraph; } #------------------------------------------------------------------------------ # get the index of text style # INPUT # @styles_paragraph : # $style_paragraph : string describing text style # OUTPUT # $i : if $i >= 0, $styles_paragraph[$i] eq $style_paragraph # if $i < 0, no entry exists in @styles_paragraph. sub get_style_paragraph { local (*styles_paragraph, $style_paragraph) = @_; my $n = @styles_paragraph; for (my $i = 0; $i < $n; $i++) { if ($styles_paragraph[$i] eq $style_paragraph) { return $i; } } -1; } #------------------------------------------------------------------------------ # add text style # INPUT # @styles_paragraph : # $p : $presentation # $position : "", "left", "center", or "right" # $left, $right, $indent : numbers in [cm] # OUTPUT # @styles_paragraph : sub add_style_paragraph { local (*styles_paragraph, $p, $position, $left, $right, $indent) = @_; if ($position eq '') { $position = "default"; } my $style_paragraph = "$position $left $right $indent"; my $n = get_style_paragraph(\@styles_paragraph, $style_paragraph); if ($n < 0) { $n = @styles_paragraph; # this is going to be the index for the new entry my $xml = '' . ' ' . ''; $p->appendElement('//office:automatic-styles', 0, $xml); push @styles_paragraph, $style_paragraph; } @styles_paragraph; } #------------------------------------------------------------------------------ # Magicpoint related sub get_mgp_options { my ($s) = @_; my ($flag_page, $flag_cont, $flag_center, $flag_left, $flag_right, $fore, $back, $size, $flag_filter, $flag_endfilter, $str_image, $str_font); $flag_page = 0; $flag_cont = 0; $flag_center = 0; $flag_left = 0; $flag_right = 0; $fore = ""; $back = ""; $size = 0; $flag_filter = 0; $flag_endfilter = 0; $str_image = ""; $str_font = ""; if (not $s =~ /^%%/) { $s =~ s/^%//; my @opts = split(/,/, $s); $n = @opts; for ($i = 0; $i < $n; $i++) { #print "# opts[$i] = ", $opts[$i], "\n"; $opts[$i] =~ s/^ //g; #print "# =>", $opts[$i], "\n"; my @opt = split(/ /, $opts[$i]); if ($opt[0] eq "page" or $opt[0] eq "PAGE") { $flag_page = 1; } elsif ($opt[0] eq 'cont' or $opt[0] eq 'CONT') { $flag_cont = 1; } elsif ($opt[0] eq 'center' or $opt[0] eq 'CENTER') { $flag_center = 1; } elsif ($opt[0] eq 'left' or $opt[0] eq 'LEFT') { $flag_left = 1; } elsif ($opt[0] eq 'right' or $opt[0] eq 'RIGHT') { $flag_right = 1; } elsif ($opt[0] eq 'fore' or $opt[0] eq 'FORE') { $opt[1] =~ s/\"//g; $fore = $opt[1]; } elsif ($opt[0] eq 'back' or $opt[0] eq 'BACK') { $opt[1] =~ s/\"//g; $back = $opt[1]; } elsif ($opt[0] eq 'size' or $opt[0] eq 'SIZE') { $size = $opt[1]; } elsif ($opt[0] eq 'filter' or $opt[0] eq 'FILTER') { $flag_filter = 1; } elsif ($opt[0] eq 'endfilter' or $opt[0] eq 'ENDFILTER') { $flag_endfilter = 1; } elsif ($opt[0] eq 'image' or $opt[0] eq 'IMAGE') { $str_image = $opts[$i]; } elsif ($opt[0] eq 'newimage' or $opt[0] eq 'NEWIMAGE') { $str_image = $opts[$i]; } elsif ($opt[0] eq 'font' or $opt[0] eq 'FONT') { $opt[1] =~ s/\"//g; $str_font = $opt[1]; } } } ($flag_page, $flag_cont, $flag_center, $flag_left, $flag_right, $fore, $back, $size, $flag_filter, $flag_endfilter, $str_image, $str_font); } #------------------------------------------------------------------------------ # bet bounding box from eps file by ghostscript's bbox device # INPUT # $file : eps filename # OUTPUT # ($x, $y) : size in cm sub bbox_eps { my $file = shift; my ($x0, $y0, $x1, $y1); open(GS, "gs -dNOPAUSE -dBATCH -sDEVICE=bbox -sOutputFile=- $file 2>&1 |"); while () { chop; if (/^\%\%BoundingBox: (\d+) +(\d+) +(\d+) +(\d+)/) { $x0 = $1; $y0 = $2; $x1 = $3; $y1 = $4; break; } } close(GS); my ($x, $y); # default dpi is set to be 72 $x = 2.54 * ($x1-$x0) / 72.0; $y = 2.54 * ($y1-$y0) / 72.0; # 1 inch = 2.54 cm ($x, $y); } #------------------------------------------------------------------------------ # parse image command line # INPUT # $line : # $path : sub parse_image { my ($line) = shift; $line =~ s/ / /g; my ($path) = shift; my @opt = split(/ /, $line); print "# line : ", $line, "\n"; my ($n, $i); $n = @opt; my ($width, $height, $img_file); $width = "2cm"; # now fixed $height = "1cm"; # now fixed $img_file = ""; my $flag_eps = 0; my ($scr_x, $scr_y) = (0, 0); my ($x_cm, $y_cm); if ($opt[0] eq "image" or $opt[0] eq "IMAGE" or $opt[0] eq "newimage" or $opt[0] eq "NEWIMAGE") { for ($i = 1; $i < $n; $i++) { if ($opt[$i] =~ /^\"(.+)\"$/) { my $tmp = $1; if ($tmp =~ /\.jpg$/ or $tmp =~ /\.jpeg$/ or $tmp =~ /\.png$/ or $tmp =~ /\.gif$/) { $img_file = $tmp; # by Image::Size my ($pix_x, $pix_y) = imgsize($path . $img_file); # (global) $dpcm : dot per cm $x_cm = $pix_x / $dpcm; $y_cm = $pix_y / $dpcm; $width = "$x_cm" . "cm"; $height = "$y_cm" . "cm"; } elsif ($tmp =~ /\.eps$/) { $flag_eps = 1; $img_file = $tmp; ($x_cm, $y_cm) = bbox_eps ($path . $img_file); $width = "$x_cm" . "cm"; $height = "$y_cm" . "cm"; } else { print "invalid image format\n"; } } elsif ($opt[$i] eq "-xscrzoom") { # zoom -- just ignore $n ++; } elsif ($opt[$i] eq "-rotate") { # zoom -- just ignore $n ++; } elsif ($opt[$i] =~ /(\d+)x(\d+)/) { # the original screensize for auto-rescale (to 1024x768) $scr_x = $1; $scr_y = $2; } } } if ($flag_eps == 1 and $scr_x != 0) { $x_cm *= 1024.0 / $scr_x; $y_cm *= 768.0 / $scr_y; $width = "$x_cm" . "cm"; $height = "$y_cm" . "cm"; } print "# size : $width, $height \n"; ($width, $height, $img_file); } #------------------------------------------------------------------------------ # remove the entry style:protect="size" # INPUT # $p : $presentation # $name : style name to remove sub remove_style_protect { my $p = shift; my $name = shift; unless ($name) { $name = "gr1"; } my @style_graphic = $p->selectElementsByAttribute ('//style:style', 'style:family', 'graphic'); my $n = @style_graphic; my $i; for ($i = 0; $i < $n; $i++) { my $style_name = $p->getAttribute ($style_graphic[$i], 'style:name'); if ($style_name eq $name) { my $prop = $style_graphic[$i]->first_child ('style:graphic-properties'); $p->removeAttribute($prop, 'style:protect'); } } 1; } #------------------------------------------------------------------------------ # convert mgp to odp # INPUT # $input : mgp file # $output : odp file # $path : current path of the mgp file sub convert_mgp_to_odp { my ($input, $output, $path) = @_; my $presentation = ooDocument (file => $output, create => 'presentation'); remove_style_protect ($presentation); my @styles_text = init_styles_text($presentation); my @styles_para = init_styles_paragraph($presentation); my $ip = 0; # page number my @page; # OODoc page my $tb; # OODoc text-box for list my $tli; # OODoc text:list-item for continuation my $flag_continue; # outside flag my $text_color, $text_bg_color; my $text_font; my $style_text; my $style_para; my $flag_body = 0; # line count for each page (from 1, ...) my $flag_outline = 0; # flag for the body entry my $title; my $flag_fltcmd = 0; # flag for the filter command my ($img_x, $img_y); open(MGPFILE, "< $input") or die "Cannot open $input"; while () { Encode::from_to($_, 'iso-2022-jp', 'utf-8'); $line = $_; $line =~ s/\n$//; if (/^%/) { my ($flag_page, $flag_cont, $flag_center, $flag_left, $flag_right, $fore, $back, $size, $flag_filter, $flag_endfilter, $str_image, $str_font) = get_mgp_options ($line); # pass parameters outside the inner loop. $flag_continue = $flag_cont; # page initialization if the new page begins if ($flag_page == 1) { print "start page $ip ($flag_page)\n"; $flag_nodefault = 0; $flag_body = 0; $flag_outline = 0; $text_color = "default"; $text_bg_color = "default"; $text_font = "normal"; $style_para = ""; $tli = undef; $flag_continue = 0; $img_x = 12.0; $img_y = 4.914; $ip ++; $page[$ip-1] = $presentation->appendDrawPage(name => "page".$ip); } # set text style my $ts; if ($fore ne "") { $text_color = $fore; } if ($back ne "") { $text_bg_color = $back; } my ($flag_bold, $flag_italic) = (0, 0); if ($str_font eq "thick") { $text_font = "bold"; $flag_bold = 1; } elsif ($str_font eq "typewriter") { $text_font = "italic"; $flag_italic = 1; } elsif ($str_font ne "") { $text_font = "normal"; } $ts = "$text_font $text_color $text_bg_color"; my $n_ts = get_style_text(\@styles_text, $ts); if ($n_ts < 0) { @styles_text = add_style_text (\@styles_text, $presentation, $flag_bold, $flag_italic, $text_color, $text_bg_color); $n_ts = @styles_text - 1; } $style_text = "T$n_ts"; # set paragraph style my $ppos = ""; if ($flag_center == 1) { $ppos = "center"; } elsif ($flag_left == 1) { $ppos = "left"; } elsif ($flag_right == 1) { $ppos = "right"; } if ($ppos ne "") { # right now, we don't check the level of list... my ($pleft, $pright, $pind) = (0, 0, 0); my $ps = "$ppos $pleft $pright $pind"; my $n_ps = get_style_paragraph(\@styles_para, $ps); if ($n_ps < 0) { @styles_para = add_style_paragraph (\@styles_para, $presentation, $ppos, $pleft, $pright, $pind); $n_ps = @styles_para - 1; } $style_para = "P$n_ps"; } # filter command if ($flag_fltcmd == 0 and $flag_filter == 1) { $flag_fltcmd = 1; } if ($flag_fltcmd == 1 and $flag_endfilter == 1) { $flag_fltcmd = 0; } # image command if ($str_image ne "") { my ($width, $height, $img_file) = parse_image($str_image, $path); add_image($presentation, $page[$ip-1], "$img_x"."cm", "$img_y"."cm", $width, $height, $path . $img_file); $img_y += $height; } } else { if ($flag_body == 0) { # print "the first line is just ignored\n"; $flag_body ++; } elsif ($flag_body == 1) { add_title($presentation, $page[$ip-1], $line); $flag_body ++; } elsif ($flag_body == 2) { # print "the third line is just ignored\n"; $flag_body ++; } else { if ($flag_outline == 0) { $flag_outline = 1; $tb = add_outline($presentation, $page[$ip-1], "1.4cm", "4.914cm", "25.199cm", "13.609cm"); } # body part if ($flag_fltcmd == 1){ # skip parsing } elsif ($flag_continue == 1) { if ($tli) { append_text($presentation, $tli, $line, $style_text); } $flag_continue = 0; } else { my ($level); # list level if (/^\t\t\t/) { $level = 4; } elsif (/^\t\t/) { $level = 3; } elsif (/^\t/) { $level = 2; } else { $level = 1; } # cut the tab $line =~ s/\t//g; $tli = make_list_item ($presentation, $tb, $level, $style_para); append_text($presentation, $tli, $line, $style_text); } } } } close MGPFILE; $presentation->save; } sub usage { print "mgp2odp is a converter from Magicpoint (mgp) file to OpenOffice (odp) file.\n"; print "USAGE: mgp2odp.pl mgp-file\n"; exit 1; } ############################################################################### # MAIN if ($#ARGV != 0) { usage(); } else { $input = $ARGV[0]; } my ($base, $path, $suffix) = File::Basename::fileparse($input, '\..*'); if ($input =~ /\.mgp$/) { $output = $base . ".odp"; } else { usage(); exit 1; } print "converting $input to $output\n"; convert_mgp_to_odp ($input, $output, $path); exit 0;