Current Path : /usr/local/lib/perl5/site_perl/5.8.9/ |
FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64 |
Current File : //usr/local/lib/perl5/site_perl/5.8.9/PDFJ.pm |
# PDFJ.pm # PDF for Japanese # 2001-3 Sey <nakajima@netstock.co.jp> package PDFJ; use PDFJ::Object; use Carp; use strict; use vars qw($VERSION @EXFUNC %Default); $VERSION = 0.72; # no I18N::Japanese; for jperl BEGIN { eval { require I18N::Japanese; }; I18N::Japanese->unimport() unless($@); } =comment # use bytes for Perl5.8 BEGIN { eval { require bytes; }; bytes->import unless $@; } =cut @EXFUNC = qw( PDFJ::Doc::Doc PDFJ::TextStyle::TStyle PDFJ::Text::Text PDFJ::NewLine::NewLine PDFJ::Outline::Outline PDFJ::Dest::Dest PDFJ::ParagraphStyle::PStyle PDFJ::Paragraph::Paragraph PDFJ::BlockStyle::BStyle PDFJ::Block::Block PDFJ::NewBlock::NewBlock PDFJ::BlockSkip::BlockSkip PDFJ::Shape::Shape PDFJ::ShapeStyle::SStyle PDFJ::Color::Color ); sub import { my($pkg, $code, $prefix) = @_; if( $code ) { croak "code argument '$code' must be 'SJIS','EUC','UTF8' or 'UNICODE'" unless $code =~ /^(SJIS|EUC|UTF8|UNICODE)$/i; $Default{Jcode} = uc($code); } $prefix ||= ""; for my $name(@EXFUNC) { my $to = caller; my $from = ""; ($from, $name) = $name =~ /^(.+)::([^:]+)$/; no strict 'refs'; *{"${to}::$prefix$name"} = \&{"${from}::$name"}; } } $Default{Jcode} = 'SJIS'; $Default{AFontEncoding} = 'WinAnsiEncoding'; $Default{BaseAFont} = 'Times-Roman'; $Default{JFontEncoding} = '90ms-RKSJ-H'; $Default{BaseJFont} = 'Ryumin-Light'; # $Default{BBox} = [-200,-331,1116,962]; $Default{BBox} = [-150,-331,1143,962]; $Default{SBoxH} = [0,-125,1000,875]; $Default{SBoxV} = [-500,-1000,500,0]; $Default{ULine} = -200; $Default{OLine} = 900; $Default{LLine} = -550; $Default{RLine} = 550; $Default{ORuby} = 950; $Default{RRuby} = 750; $Default{HBaseShift} = 0.125; $Default{HBaseHeight} = 0.875; $Default{ParaPreSkipRatio} = 0.5; $Default{ParaPostSkipRatio} = 0.5; $Default{SlantRatio} = 0.2; $Default{HDotXShift} = 0; $Default{HDotYShift} = 0.7; $Default{HDot}{SJIS} = "\x81\x45"; $Default{HDot}{EUC} = "\xa1\xa6"; $Default{HDot}{UNICODE} = "\x30\xfb"; $Default{HDot}{UTF8} = "\x30\xfb"; $Default{VDotXShift} = 0.5; $Default{VDotYShift} = -0.3; $Default{VDot}{SJIS} = "\x81\x41"; $Default{VDot}{EUC} = "\xa1\xa2"; $Default{VDot}{UNICODE} = "\x30\x01"; $Default{VDot}{UTF8} = "\x30\x01"; $Default{VHShift} = 0.8; $Default{VAShift} = -0.33; $Default{SuffixSize} = 0.6; $Default{USuffixRise} = 0.5; $Default{LSuffixRise} = -0.15; $Default{HNote} = 990; $Default{VNote} = 750; $Default{Fonts} = {qw( Courier a Courier-Bold a Courier-BoldOblique a Courier-Oblique a Helvetica a Helvetica-Bold a Helvetica-BoldOblique a Helvetica-Oblique a Times-Bold a Times-BoldItalic a Times-Italic a Times-Roman a Ryumin-Light j GothicBBB-Medium j )}; $Default{Encodings} = {qw( WinAnsiEncoding a MacRomanEncoding a 83pv-RKSJ-H js 90pv-RKSJ-H js 90ms-RKSJ-H js 90ms-RKSJ-V js Add-RKSJ-H js Add-RKSJ-V js Ext-RKSJ-H js Ext-RKSJ-V js EUC-H je EUC-V je UniJIS-UCS2-HW-H ju UniJIS-UCS2-HW-V ju )}; $Default{JFD}{'Ryumin-Light'} = { Type => name('FontDescriptor'), Ascent => 723, CapHeight => 709, Descent => -241, Flags => 6, FontBBox => [-170,-331,1024,903], FontName => name('Ryumin-Light'), ItalicAngle => 0, StemV => 69, XHeight => 450, Style => { Panose => string( value => '010502020300000000000000', outputtype => 'hexliteral') }, }; $Default{JFD}{'GothicBBB-Medium'} = { Type => name('FontDescriptor'), Ascent => 752, CapHeight => 737, Descent => -271, Flags => 4, FontBBox => [-174,-268,1001,944], FontName => name('GothicBBB-Medium'), ItalicAngle => 0, StemV => 99, XHeight => 553, Style => { Panose => string( value => '0801020b0500000000000000', outputtype => 'hexliteral') } }; # character class (based on JIS X 4051) # 0: begin paren # 1: end paren # 2: not at top of line # 3: ?! # 4: dot # 5: punc # 6: leader # 7: pre unit # 8: post unit # 9: zenkaku space # 10: hirakana # 11: japanese # 12: suffixed # 13: rubied # 14: number # 15: unit # 16: space # 17: ascii $Default{Class}{SJIS} = { # begin paren "\x81\x65" => 0, "\x81\x67" => 0, "\x81\x69" => 0, "\x81\x6b" => 0, "\x81\x6d" => 0, "\x81\x6f" => 0, "\x81\x71" => 0, "\x81\x73" => 0, "\x81\x75" => 0, "\x81\x77" => 0, "\x81\x79" => 0, # end paren "\x81\x41" => 1, "\x81\x43" => 1, "\x81\x66" => 1, "\x81\x68" => 1, "\x81\x6a" => 1, "\x81\x6c" => 1, "\x81\x6e" => 1, "\x81\x70" => 1, "\x81\x72" => 1, "\x81\x74" => 1, "\x81\x76" => 1, "\x81\x78" => 1, "\x81\x7a" => 1, # not at top of line "\x81\x52" => 2, "\x81\x53" => 2, "\x81\x54" => 2, "\x81\x55" => 2, "\x81\x58" => 2, "\x81\x5b" => 2, "\x82\x9f" => 2, "\x82\xa1" => 2, "\x82\xa3" => 2, "\x82\xa5" => 2, "\x82\xa7" => 2, "\x82\xc1" => 2, "\x82\xe1" => 2, "\x82\xe3" => 2, "\x82\xe5" => 2, "\x82\xec" => 2, "\x83\x40" => 2, "\x83\x42" => 2, "\x83\x44" => 2, "\x83\x46" => 2, "\x83\x48" => 2, "\x83\x62" => 2, "\x83\x83" => 2, "\x83\x85" => 2, "\x83\x87" => 2, "\x83\x8e" => 2, "\x83\x95" => 2, "\x83\x96" => 2, # ?! "\x81\x48" => 3, "\x81\x49" => 3, # dot "\x81\x45" => 4, "\x81\x46" => 4, "\x81\x47" => 4, # punc "\x81\x42" => 5, "\x81\x44" => 5, # leader "\x81\x5c" => 6, "\x81\x63" => 6, "\x81\x64" => 6, # pre unit "\x81\x8f" => 7, "\x81\x90" => 7, "\x81\x92" => 7, # post unit "\x81\x8b" => 8, "\x81\x8c" => 8, "\x81\x8d" => 8, "\x81\x91" => 8, "\x81\x93" => 8, "\x81\xf1" => 8, # zenkaku space "\x81\x40" => 9, }; $Default{PreShift}{SJIS} = { # begin paren "\x81\x65" => 500, "\x81\x67" => 500, "\x81\x69" => 500, "\x81\x6b" => 500, "\x81\x6d" => 500, "\x81\x6f" => 500, "\x81\x71" => 500, "\x81\x73" => 500, "\x81\x75" => 500, "\x81\x77" => 500, "\x81\x79" => 500, # dot "\x81\x45" => 250, "\x81\x46" => 250, "\x81\x47" => 250, }; $Default{PostShift}{SJIS} = { # end paren "\x81\x41" => 500, "\x81\x43" => 500, "\x81\x66" => 500, "\x81\x68" => 500, "\x81\x6a" => 500, "\x81\x6c" => 500, "\x81\x6e" => 500, "\x81\x70" => 500, "\x81\x72" => 500, "\x81\x74" => 500, "\x81\x76" => 500, "\x81\x78" => 500, "\x81\x7a" => 500, # dot "\x81\x45" => 250, "\x81\x46" => 250, "\x81\x47" => 250, # punc "\x81\x42" => 500, "\x81\x44" => 500, # post unit "\x81\x8b" => 500, "\x81\x8c" => 500, "\x81\x8d" => 500, }; $Default{Class}{EUC} = { # begin paren "\xa1\xc6" => 0, "\xa1\xc8" => 0, "\xa1\xca" => 0, "\xa1\xcc" => 0, "\xa1\xce" => 0, "\xa1\xd0" => 0, "\xa1\xd2" => 0, "\xa1\xd4" => 0, "\xa1\xd6" => 0, "\xa1\xd8" => 0, "\xa1\xda" => 0, # end paren "\xa1\xa2" => 1, "\xa1\xa4" => 1, "\xa1\xc7" => 1, "\xa1\xc9" => 1, "\xa1\xcb" => 1, "\xa1\xcd" => 1, "\xa1\xcf" => 1, "\xa1\xd1" => 1, "\xa1\xd3" => 1, "\xa1\xd5" => 1, "\xa1\xd7" => 1, "\xa1\xd9" => 1, "\xa1\xdb" => 1, # not at top of line "\xa1\xb3" => 2, "\xa1\xb4" => 2, "\xa1\xb5" => 2, "\xa1\xb6" => 2, "\xa1\xb9" => 2, "\xa1\xbc" => 2, "\xa4\xa1" => 2, "\xa4\xa3" => 2, "\xa4\xa5" => 2, "\xa4\xa7" => 2, "\xa4\xa9" => 2, "\xa4\xc3" => 2, "\xa4\xe3" => 2, "\xa4\xe5" => 2, "\xa4\xe7" => 2, "\xa4\xee" => 2, "\xa5\xa1" => 2, "\xa5\xa3" => 2, "\xa5\xa5" => 2, "\xa5\xa7" => 2, "\xa5\xa9" => 2, "\xa5\xc3" => 2, "\xa5\xe3" => 2, "\xa5\xe5" => 2, "\xa5\xe7" => 2, "\xa5\xee" => 2, "\xa5\xf5" => 2, "\xa5\xf6" => 2, # ?! "\xa1\xa9" => 3, "\xa1\xaa" => 3, # dot "\xa1\xa6" => 4, "\xa1\xa7" => 4, "\xa1\xa8" => 4, # punc "\xa1\xa3" => 5, "\xa1\xa5" => 5, # leader "\xa1\xbd" => 6, "\xa1\xc4" => 6, "\xa1\xc5" => 6, # pre unit "\xa1\xef" => 7, "\xa1\xf0" => 7, "\xa1\xf2" => 7, # post unit "\xa1\xeb" => 8, "\xa1\xec" => 8, "\xa1\xed" => 8, "\xa1\xf1" => 8, "\xa1\xf3" => 8, "\xa2\xf3" => 8, # zenkaku space "\xa1\xa1" => 9, }; $Default{PreShift}{EUC} = { # begin paren "\xa1\xc6" => 500, "\xa1\xc8" => 500, "\xa1\xca" => 500, "\xa1\xcc" => 500, "\xa1\xce" => 500, "\xa1\xd0" => 500, "\xa1\xd2" => 500, "\xa1\xd4" => 500, "\xa1\xd6" => 500, "\xa1\xd8" => 500, "\xa1\xda" => 500, # dot "\xa1\xa6" => 250, "\xa1\xa7" => 250, "\xa1\xa8" => 250, }; $Default{PostShift}{EUC} = { # end paren "\xa1\xa2" => 500, "\xa1\xa4" => 500, "\xa1\xc7" => 500, "\xa1\xc9" => 500, "\xa1\xcb" => 500, "\xa1\xcd" => 500, "\xa1\xcf" => 500, "\xa1\xd1" => 500, "\xa1\xd3" => 500, "\xa1\xd5" => 500, "\xa1\xd7" => 500, "\xa1\xd9" => 500, "\xa1\xdb" => 500, # dot "\xa1\xa6" => 250, "\xa1\xa7" => 250, "\xa1\xa8" => 250, # punc "\xa1\xa3" => 500, "\xa1\xa5" => 500, # post unit "\xa1\xeb" => 500, "\xa1\xec" => 500, "\xa1\xed" => 500, }; $Default{Class}{UNICODE} = { # begin paren "\x20\x18" => 0, "\x20\x1c" => 0, "\xff\x08" => 0, "\x30\x14" => 0, "\xff\x3b" => 0, "\xff\x5b" => 0, "\x30\x08" => 0, "\x30\x0a" => 0, "\x30\x0c" => 0, "\x30\x0e" => 0, "\x30\x10" => 0, # end paren "\x30\x01" => 1, "\xff\x0c" => 1, "\x20\x19" => 1, "\x20\x1d" => 1, "\xff\x09" => 1, "\x30\x15" => 1, "\xff\x3d" => 1, "\xff\x5d" => 1, "\x30\x09" => 1, "\x30\x0b" => 1, "\x30\x0d" => 1, "\x30\x0f" => 1, "\x30\x11" => 1, # not at top of line "\x30\xfd" => 2, "\x30\xfe" => 2, "\x30\x9d" => 2, "\x30\x9e" => 2, "\x30\x05" => 2, "\x30\xfc" => 2, "\x30\x41" => 2, "\x30\x43" => 2, "\x30\x45" => 2, "\x30\x47" => 2, "\x30\x49" => 2, "\x30\x63" => 2, "\x30\x83" => 2, "\x30\x85" => 2, "\x30\x87" => 2, "\x30\x8e" => 2, "\x30\xa1" => 2, "\x30\xa3" => 2, "\x30\xa5" => 2, "\x30\xa7" => 2, "\x30\xa9" => 2, "\x30\xc3" => 2, "\x30\xe3" => 2, "\x30\xe5" => 2, "\x30\xe7" => 2, "\x30\xee" => 2, "\x30\xf5" => 2, "\x30\xf6" => 2, # ?! "\xff\x1f" => 3, "\xff\x01" => 3, # dot "\x30\xfb" => 4, "\xff\x1a" => 4, "\xff\x1b" => 4, # punc "\x30\x02" => 5, "\xff\x0e" => 5, # leader "\x20\x15" => 6, "\x20\x26" => 6, "\x20\x25" => 6, # pre unit "\xff\xe5" => 7, "\xff\x04" => 7, "\xff\xe1" => 7, # post unit "\x00\xb0" => 8, "\x20\x32" => 8, "\x20\x33" => 8, "\xff\xe0" => 8, "\xff\x05" => 8, "\x20\x30" => 8, # zenkaku space "\x30\x00" => 9, }; $Default{PreShift}{UNICODE} = { # begin paren "\x20\x18" => 500, "\x20\x1c" => 500, "\xff\x08" => 500, "\x30\x14" => 500, "\xff\x3b" => 500, "\xff\x5b" => 500, "\x30\x08" => 500, "\x30\x0a" => 500, "\x30\x0c" => 500, "\x30\x0e" => 500, "\x30\x10" => 500, # dot "\x30\xfb" => 250, "\xff\x1a" => 250, "\xff\x1b" => 250, }; $Default{PostShift}{UNICODE} = { # end paren "\x30\x01" => 500, "\xff\x0c" => 500, "\x20\x19" => 500, "\x20\x1d" => 500, "\xff\x09" => 500, "\x30\x15" => 500, "\xff\x3d" => 500, "\xff\x5d" => 500, "\x30\x09" => 500, "\x30\x0b" => 500, "\x30\x0d" => 500, "\x30\x0f" => 500, "\x30\x11" => 500, # dot "\x30\xfb" => 250, "\xff\x1a" => 250, "\xff\x1b" => 250, # punc "\x30\x02" => 500, "\xff\x0e" => 500, # post unit "\x00\xb0" => 500, "\x20\x32" => 500, "\x20\x33" => 500, }; # glue width # each element means [min, normal, max, preference] # ruby overlap feature is omitted sub GlueNon { [0, 0, 0] } sub Glue004 { [0, 0, 250] } sub Glue0443 { [0, 250, 250, 3] } sub Glue0223 { [0, 500, 500, 3] } sub Glue0222 { [0, 500, 500, 2] } sub Glue222 { [500, 500, 500] } sub Glue844 { [125, 250, 250] } sub Glue8421 { [125, 250, 500, 1] } sub Glue266 { [500, 750, 750] } $Default{Glue} = [ # 0: begin paren [ GlueNon, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc GlueNon, # 6: leader GlueNon, # 7: pre unit GlueNon, # 8: post unit GlueNon, # 9: zenkaku space GlueNon, # 10: hirakana GlueNon, # 11: japanese GlueNon, # 12: suffixed GlueNon, # 13: rubied GlueNon, # 14: number GlueNon, # 15: unit Glue004, # 16: space GlueNon, # 17: ascii ], # 1: end paren [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue0222, # 2: not at top of line Glue0222, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue0222, # 6: leader Glue0222, # 7: pre unit Glue0222, # 8: post unit GlueNon, # 9: zenkaku space Glue0222, # 10: hirakana Glue0222, # 11: japanese Glue0222, # 12: suffixed Glue0222, # 13: rubied Glue0222, # 14: number Glue0222, # 15: unit Glue0222, # 16: space Glue0222, # 17: ascii ], # 2: not at top of line [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue8421, # 12: suffixed Glue004, # 13: rubied Glue8421, # 14: number Glue8421, # 15: unit Glue004, # 16: space Glue8421, # 17: ascii ], # 3: ?! [ Glue0222, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc GlueNon, # 6: leader GlueNon, # 7: pre unit GlueNon, # 8: post unit GlueNon, # 9: zenkaku space GlueNon, # 10: hirakana GlueNon, # 11: japanese Glue8421, # 12: suffixed GlueNon, # 13: rubied Glue8421, # 14: number Glue8421, # 15: unit Glue004, # 16: space Glue8421, # 17: ascii ], # 4: dot [ Glue0443, # 0: begin paren Glue0443, # 1: end paren Glue0443, # 2: not at top of line Glue0443, # 3: ?! Glue0223, # 4: dot Glue0443, # 5: punc Glue0443, # 6: leader Glue0443, # 7: pre unit Glue0443, # 8: post unit Glue0443, # 9: zenkaku space Glue0443, # 10: hirakana Glue0443, # 11: japanese Glue0443, # 12: suffixed Glue0443, # 13: rubied Glue0443, # 14: number Glue0443, # 15: unit Glue0443, # 16: space Glue0443, # 17: ascii ], # 5: punc [ Glue222, # 0: begin paren GlueNon, # 1: end paren Glue222, # 2: not at top of line Glue222, # 3: ?! Glue266, # 4: dot GlueNon, # 5: punc Glue222, # 6: leader Glue222, # 7: pre unit Glue222, # 8: post unit Glue222, # 9: zenkaku space Glue222, # 10: hirakana Glue222, # 11: japanese Glue222, # 12: suffixed Glue222, # 13: rubied Glue222, # 14: number Glue222, # 15: unit Glue222, # 16: space Glue222, # 17: ascii ], # 6: leader [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc GlueNon, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue004, # 12: suffixed Glue004, # 13: rubied Glue004, # 14: number Glue004, # 15: unit Glue004, # 16: space Glue004, # 17: ascii ], # 7: pre unit [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue004, # 12: suffixed Glue004, # 13: rubied GlueNon, # 14: number Glue004, # 15: unit Glue004, # 16: space Glue004, # 17: ascii ], # 8: post unit [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue004, # 12: suffixed Glue004, # 13: rubied Glue004, # 14: number Glue004, # 15: unit Glue004, # 16: space Glue004, # 17: ascii ], # 9: zenkaku space [ GlueNon, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc GlueNon, # 6: leader GlueNon, # 7: pre unit GlueNon, # 8: post unit GlueNon, # 9: zenkaku space GlueNon, # 10: hirakana GlueNon, # 11: japanese GlueNon, # 12: suffixed GlueNon, # 13: rubied GlueNon, # 14: number GlueNon, # 15: unit Glue004, # 16: space GlueNon, # 17: ascii ], # 10: hirakana [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue8421, # 12: suffixed Glue004, # 13: rubied Glue8421, # 14: number Glue8421, # 15: unit Glue004, # 16: space Glue8421, # 17: ascii ], # 11: japanese [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue8421, # 12: suffixed Glue004, # 13: rubied Glue8421, # 14: number Glue8421, # 15: unit Glue004, # 16: space Glue8421, # 17: ascii ], # 12: suffixed [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue8421, # 10: hirakana Glue8421, # 11: japanese GlueNon, # 12: suffixed Glue8421, # 13: rubied Glue004, # 14: number Glue004, # 15: unit Glue004, # 16: space Glue844, # 17: ascii ], # 13: rubied [ Glue0222, # 0: begin paren GlueNon, # 1: end paren Glue004, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue8421, # 12: suffixed GlueNon, # 13: rubied Glue8421, # 14: number Glue8421, # 15: unit Glue004, # 16: space Glue8421, # 17: ascii ], # 14: number [ Glue0222, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit GlueNon, # 8: post unit GlueNon, # 9: zenkaku space Glue8421, # 10: hirakana Glue8421, # 11: japanese Glue004, # 12: suffixed Glue8421, # 13: rubied GlueNon, # 14: number Glue8421, # 15: unit Glue004, # 16: space GlueNon, # 17: ascii ], # 15: unit [ Glue0222, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue8421, # 10: hirakana Glue8421, # 11: japanese Glue004, # 12: suffixed Glue8421, # 13: rubied Glue8421, # 14: number GlueNon, # 15: unit Glue004, # 16: space GlueNon, # 17: ascii ], # 16: space [ Glue0222, # 0: begin paren Glue004, # 1: end paren Glue004, # 2: not at top of line Glue004, # 3: ?! Glue0443, # 4: dot Glue004, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit Glue004, # 9: zenkaku space Glue004, # 10: hirakana Glue004, # 11: japanese Glue004, # 12: suffixed Glue004, # 13: rubied Glue004, # 14: number Glue004, # 15: unit Glue004, # 16: space Glue004, # 17: ascii ], # 17: ascii [ Glue0222, # 0: begin paren GlueNon, # 1: end paren GlueNon, # 2: not at top of line GlueNon, # 3: ?! Glue0443, # 4: dot GlueNon, # 5: punc Glue004, # 6: leader Glue004, # 7: pre unit Glue004, # 8: post unit GlueNon, # 9: zenkaku space Glue8421, # 10: hirakana Glue8421, # 11: japanese Glue844, # 12: suffixed Glue8421, # 13: rubied GlueNon, # 14: number GlueNon, # 15: unit Glue004, # 16: space GlueNon, # 17: ascii ], ]; $Default{Splittable} = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0], ]; # 1 if not at begin of line $Default{NoBOL} = [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; # 1 if not at end of line $Default{NoEOL} = [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; #-------------------------------------------------------------------------- package PDFJ::Util; use Carp; use FileHandle; use strict; my $TeXHyphenObj; sub hyphenate { my($word) = @_; require TeX::Hyphen; $TeXHyphenObj = TeX::Hyphen->new unless $TeXHyphenObj; $TeXHyphenObj->hyphenate($word); } sub uriencode { my($str) = @_; $str =~ s/([^;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()])/sprintf("%%%02X",ord($1))/ge unless $str =~ /%[0-9a-fA-F]{2}/; $str; } my $SubsetTag; sub subsettag { unless( $SubsetTag ) { for(1..6) { $SubsetTag .= chr(ord('A') + int(rand(26))); } } $SubsetTag++; } sub ttfopen { my($ttffile, $encoding) = @_; require PDFJ::TTF; my $ttf; if( $ttffile =~ /(\.ttc):(\d)$/i ) { my $ttcfile = $`.$1; my $num = $2; my $ttc = new PDFJ::TTC $ttcfile; $ttf = $ttc->select($num); } else { $ttf = new PDFJ::TTF $ttffile; } croak "cannot open $ttffile" unless $ttf; $ttf->read_table(':all'); $ttf; } sub deflate { my($src) = @_; my $reader = reader($src) or return; eval { require Compress::Zlib; }; #return if $@; croak $@ if $@; my $OK = Compress::Zlib::Z_OK(); my $d = Compress::Zlib::deflateInit() or return; my($len, $data, $result); while( $len = &$reader($data, 1024) ) { my($deflated, $status) = $d->deflate($data); return unless $status == $OK; $result .= $deflated; } my($deflated, $status) = $d->flush(); return unless $status == $OK; $result .= $deflated; $result; } sub deflate_ascii85encode { my($src) = @_; my($result, $deflated); my $temp = deflate($src); if( $temp ) { $result = ascii85encode(\$temp); $deflated = 1; } else { $result = ascii85encode($src); } ($result, $deflated); } sub ascii85encode { my($src) = @_; my $reader = reader($src) or return; my($len, $data, $result); while( $len = &$reader($data, 4) ) { if( $len == 4 ) { my $resultchunk = ascii85chunk($data); $resultchunk = 'z' if $resultchunk eq '!!!!!'; $result .= $resultchunk; } else { for( my $j = 4; $j > $len; $j-- ) { $data .= "\000"; } $result .= substr ascii85chunk($data), 0, $len + 1; last; } } $result .= '~>'; $result; } sub ascii85chunk { my($chunk) = @_; $chunk = unpack("N", $chunk); my $resultchunk; $resultchunk .= chr(int($chunk / (85 ** 4)) + 33); $chunk = $chunk % (85 ** 4); $resultchunk .= chr(int($chunk / (85 ** 3)) + 33); $chunk = $chunk % (85 ** 3); $resultchunk .= chr(int($chunk / (85 ** 2)) + 33); $chunk = $chunk % (85 ** 2); $resultchunk .= chr(int($chunk / 85) + 33); $chunk = $chunk % 85; $resultchunk .= chr($chunk + 33); $resultchunk; } sub tounicode { my($str, $init) = @_; require PDFJ::Unicode; my $result = $init ? "\xfe\xff" : ""; if( $PDFJ::Default{Jcode} eq 'SJIS' ) { $result .= PDFJ::Unicode::sjistounicode($str); } elsif( $PDFJ::Default{Jcode} eq 'EUC' ) { $result .= PDFJ::Unicode::euctounicode($str); } elsif( $PDFJ::Default{Jcode} eq 'UTF8' ) { $result .= PDFJ::Unicode::utf8tounicode($str); } elsif( $PDFJ::Default{Jcode} eq 'UNICODE' ) { $result .= $str; } $result; } sub reader { my($src) = @_; if( ref($src) eq 'SCALAR' ) { my $pos = 0; return sub { $_[0] = substr $$src, $pos, $_[1]; my $len = length $_[0]; $pos += $len; $len; }; } else { my $handle = FileHandle->new($src) or return; binmode $handle; return sub { read $handle, $_[0], $_[1]; }; } } sub methodargs { my $names = shift; if( @_ == 2 && ref($_[1]) eq 'HASH' ) { ($_[0], @{$_[1]}{@$names}); } else { @_; } } sub listargs2ref { my($pre, $post) = (shift, shift); return @_ if @_ < $pre + $post; my @list = $post ? splice(@_, $pre, -$post) : splice(@_, $pre); my $list = (@list == 1 && ref($list[0]) eq 'ARRAY') ? $list[0] : \@list; splice @_, $pre, 0, $list; @_; } sub strstyle2hashref { my($style) = @_; my($result) = _strstyle2hashref($style); $result; } sub _strstyle2hashref { my($style, $brace) = @_; return ($style) if ref($style); my %result; while( $style =~ /(\S+?)\s*:\s*/ ) { my($name, $rest) = ($1, $'); my($value, $delim); if( $rest =~ s/^\{// ) { ($value, $style) = _strstyle2hashref($rest, 1); } elsif( $brace ) { ($value, $delim) = $rest =~ /(.*?)(;|\}|$)/; $style = $'; $value =~ s/\s+$//; } else { ($value, $delim) = $rest =~ /(.*?)(;|$)/; $style = $'; $value =~ s/\s+$//; } $result{$name} = $value; last if $delim && $delim eq '}'; } (\%result, $style); } #-------------------------------------------------------------------------- package PDFJ::Doc; use strict; use Carp; use FileHandle; use PDFJ::Object; sub Doc { PDFJ::Doc->new(@_) } sub new { my($class, $version, $pagewidth, $pageheight) = PDFJ::Util::methodargs([qw(version pagewidth pageheight)], @_); my $objtable = PDFJ::ObjTable->new; my $self = bless { version => $version, objtable => $objtable, pagewidth => $pagewidth, pageheight => $pageheight, pagelist => [], pageobjlist => [], fontlist => {}, imagelist => {}, jcidsysteminfo => undef, jfontdescriptor => {}, filter => 'f', # a:ascii85, f:flate, af:both subsettag => PDFJ::Util::subsettag(), }, $class; $self->{pagetree} = $self->indirect(dictionary({ Type => name('Pages'), Kids => [], Count => 0, })); $self->{catalog} = $self->indirect(dictionary({ Type => name('Catalog'), Pages => $self->{pagetree}, })); $self; } sub pagewidth { my($self) = @_; $self->{pagewidth}; } sub pageheight { my($self) = @_; $self->{pageheight}; } sub filter { my($self, $filter) = PDFJ::Util::methodargs([qw(filter)], @_); if( defined $filter ) { $self->{filter} = $filter; # a:ascii85, f:flate, af:both } $self->{filter}; } sub _add_outline { my($self, $title, $dest, $parent) = @_; $title = PDFJ::Util::tounicode($title, 1) if $title =~ /[\x80-\xff]/; my $lastitem = $parent->get('Last'); my $newitem; if( $lastitem ) { $newitem = $self->indirect(dictionary({ Title => string($title), Parent => $parent, Prev => $lastitem, Count => 0, })); $newitem->set('Dest', $dest) if $dest; $lastitem->set('Next', $newitem); $parent->set('Last', $newitem); } else { $newitem = $self->indirect(dictionary({ Title => string($title), Parent => $parent, Count => 0, })); $newitem->set('Dest', $dest) if $dest; $parent->set('First', $newitem); $parent->set('Last', $newitem); } while( $parent ) { $parent->get('Count')->add(1); $parent = $parent->get('Parent'); } $newitem; } sub add_outline { my($self, $title, $dest, $level) = @_; unless( $self->{outline} ) { $self->{outline} = $self->indirect(dictionary({ Type => name('Outlines'), Count => 0, })); $self->{catalog}->set('Outlines', $self->{outline}); $self->{catalog}->set('PageMode', name('UseOutlines')); } my $parent = $self->{outline}; while( $level-- ) { $parent = $parent->get('Last') ? $parent->get('Last') : $self->_add_outline('', undef, $parent); } $self->_add_outline($title, $dest, $parent); } sub add_dest { my($self, $name, $dest) = @_; $self->{dest}{$name} = $dest; } sub dest { my($self, $name) = @_; $self->{dest}{$name}; } sub indirect { my($self, $obj) = @_; $obj->indirect($self->{objtable}); } sub print { my($self, $file) = PDFJ::Util::methodargs([qw(file)], @_); $self->_solve_link; $self->_complete_subsetfont; my $handle = FileHandle->new(">$file"); return unless $handle; my $fobj = PDFJ::File->new($self->{version}, $handle, $self->{objtable}, $self->{catalog}); $fobj->print; close $handle; } sub new_page { my($self, $pagewidth, $pageheight) = PDFJ::Util::methodargs([qw(pagewidth pageheight)], @_); PDFJ::Page->new($self, $pagewidth, $pageheight); } sub get_page { my($self, $idx) = PDFJ::Util::methodargs([qw(number)], @_); $self->{pageobjlist}->[$idx]; } sub get_lastpagenum { my $self = shift; scalar @{$self->{pagelist}}; } sub new_font { my($self, $basefont, $encoding, $abasefont, $aencoding) = PDFJ::Util::methodargs([qw(basefont encoding abasefont aencoding)], @_); if( $abasefont ) { $self->new_combofont($basefont, $encoding, $abasefont, $aencoding); } else { $self->new_singlefont($basefont, $encoding); } } sub new_singlefont { my($self, $basefont, $encoding) = @_; $basefont ||= $PDFJ::Default{BaseAFont}; my $type = $PDFJ::Default{Fonts}{$basefont}; if( $type eq 'a' ) { new_afont($self, $basefont, $encoding); } elsif( $type eq 'j' ) { new_jfont($self, $basefont, $encoding); } elsif( $basefont =~ /\.ttf$/i || $basefont =~ /\.ttc:\d$/i ) { if( $PDFJ::Default{Encodings}{$encoding} eq 'a' ) { new_afont($self, $basefont, $encoding); } else { new_jfont($self, $basefont, $encoding); } } else { croak "unknown font: $basefont"; } } sub new_combofont { my($self, $zbase, $zenc, $hbase, $henc) = @_; my $hfont = UNIVERSAL::isa($hbase, "PDFJ::AFont") ? $hbase : new_afont($self, $hbase, $henc); new_jfont($self, $zbase, $zenc, $hfont); } sub new_afont { my($self, $basefont, $encoding) = @_; $basefont ||= $PDFJ::Default{BaseAFont}; $encoding ||= $PDFJ::Default{AFontEncoding}; croak "encoding type mismatch" unless $PDFJ::Default{Encodings}{$encoding} eq 'a'; if( $basefont =~ /\.ttf$/i || $basefont =~ /\.ttc:\d$/i ) { PDFJ::AFont->new_ttf($self, $basefont, $encoding); } else { PDFJ::AFont->new_std($self, $basefont, $encoding); } } sub new_jfont { my($self, $basefont, $encoding, $hfont) = @_; $basefont ||= $PDFJ::Default{BaseJFont}; $encoding ||= $PDFJ::Default{JFontEncoding}; croak "encoding type mismatch" unless $PDFJ::Default{Encodings}{$encoding} eq ($PDFJ::Default{Jcode} eq 'SJIS' ? 'js' : $PDFJ::Default{Jcode} eq 'EUC' ? 'je' : 'ju'); if( $basefont =~ /\.ttf$/i || $basefont =~ /\.ttc:\d$/i ) { PDFJ::JFont->new_ttf($self, $basefont, $encoding, $hfont); } else { PDFJ::JFont->new_std($self, $basefont, $encoding, $hfont); } } sub new_image { my($self, $src, $pxwidth, $pxheight, $width, $height, $padding, $colorspace) = PDFJ::Util::methodargs([qw(src pxwidth pxheight width height padding colorspace)], @_); PDFJ::Image->new($self, $src, $pxwidth, $pxheight, $width, $height, $padding, $colorspace); } sub italic { my($self, @args) = PDFJ::Util::methodargs([qw(base decorated)], @_); $self->_deco('italic', @args); } sub bold { my($self, @args) = PDFJ::Util::methodargs([qw(base decorated)], @_); $self->_deco('bold', @args); } # internal methods sub _deco { my($self, $style, @args) = @_; # $style: italic, bold croak "arguments must be even" if @args % 2; while( @args ) { my $base = shift @args; my $deco = shift @args; if( $base->isa("PDFJ::AFont") ) { croak "font type mismatch" unless $deco->isa("PDFJ::AFont"); $self->{$style}{$base->{name}} = $deco->{name}; } elsif( $base->isa("PDFJ::JFont") ) { croak "font type mismatch" unless $deco->isa("PDFJ::JFont"); if( $base->{combo} ) { croak "font combo type mismatch" unless $deco->{combo}; $self->{$style}{$base->{zname}, $base->{hname}} = join($;, $deco->{zname}, $deco->{hname}); } else { croak "font combo type mismatch" if $deco->{combo}; $self->{$style}{$base->{zname}} = $deco->{zname}; } } } } sub _bolditalicname { my($self, $name, $style) = @_; # $style: PDFJ::TStyle object my $dname = $name; $dname = $self->{italic}{$dname} || $dname if $style->{italic}; $dname = $self->{bold}{$dname} || $dname if $style->{bold}; $dname; } sub _bold { my($self, $name) = @_; $self->{fontlist}{$self->{bold}{$name}} } sub _solve_link { my $self = shift; for my $pageobj(@{$self->{pageobjlist}}) { $pageobj->solve_link; } } sub _complete_subsetfont { my $self = shift; for my $name(keys %{$self->{subsetttf}}) { my $sttf = $self->{subsetttf}{$name}; my $ttf = $sttf->{ttf}; my $direction = $sttf->{direction}; my @unicodes = sort keys %{$sttf->{subset_unicodes}}; my $font = $self->{fontlist}{$name}; my($subset, $c2g, $cidset) = $ttf->subset($direction, @unicodes); my $size = length $subset; my($encoded, $filter) = $self->_makestream(\$subset); croak "cannot encode ttf subset data" unless $encoded; $font->get('DescendantFonts')->get(0)->get('FontDescriptor')->set( FontFile2 => $self->indirect(stream(dictionary => { Filter => $filter, Length => length($encoded), Length1 => $size, }, stream => $encoded))); ($encoded, $filter) = $self->_makestream(\$c2g); croak "cannot encode ttf subset cidtogidmap" unless $encoded; $font->get('DescendantFonts')->get(0)->set( CIDToGIDMap => $self->indirect(stream(dictionary => { Filter => $filter, Length => length($encoded), }, stream => $encoded))); if(0) { ($encoded, $filter) = $self->_makestream(\$cidset); croak "cannot encode ttf subset cidset" unless $encoded; $font->get('DescendantFonts')->get(0)->get('FontDescriptor')->set( CIDSet => $self->indirect(stream(dictionary => { Filter => $filter, Length => length($encoded), }, stream => $encoded))); } } } sub _makestream { my($self, $src, @addfilters) = @_; my($encoded, $deflated, @filters); if( $self->filter =~ /af/ ) { ($encoded, $deflated) = PDFJ::Util::deflate_ascii85encode($src); return unless $encoded; @filters = $deflated ? qw(ASCII85Decode FlateDecode) : qw(ASCII85Decode); } elsif( $self->filter =~ /f/ ) { $encoded = PDFJ::Util::deflate($src) or return; @filters = qw(FlateDecode); } elsif( $self->filter =~ /a/ ) { $encoded = PDFJ::Util::ascii85encode($src) or return; @filters = qw(ASCII85Decode); } else { return; } push @filters, @addfilters if @addfilters; my $filter = @filters > 1 ? [map {name($_)} @filters] : name($filters[0]); ($encoded, $filter); } sub _nextsubsettag { my $self = shift; $self->{subsettag}++; } sub _nextfontnum { my $self = shift; 1 + scalar keys %{$self->{fontlist}}; } sub _registfont { my($self, $fontobj) = @_; my $baseorttf = $fontobj->{ttffile} || $fontobj->{basefont}; my $encoding = $fontobj->{encoding}; my $name = $fontobj->{name} || $fontobj->{zname}; my $font = $fontobj->{font} || $fontobj->{zfont}; $self->{fontname}{$baseorttf, $encoding} = $name; $self->{fontlist}{$name} = $font; if( $fontobj->{combo} ) { $self->{fontobjlist}{$name, $fontobj->{hname}} = $fontobj; } else { $self->{fontobjlist}{$name} = $fontobj; } } sub _registsubset { my($self, %args) = @_; $args{subset_unicodes} = {}; $self->{subsetttf}{$args{name}} = \%args; } sub _subsetttf { my($self, $name) = @_; $self->{subsetttf}{$name}; } sub _fontname { my($self, $baseorttf, $encoding) = @_; $self->{fontname}{$baseorttf, $encoding}; } sub _font { my($self, $name) = @_; $self->{fontlist}{$name}; } sub _fontobj { my($self, $name, $hname) = @_; $hname ? $self->{fontobjlist}{$name, $hname} : $self->{fontobjlist}{$name}; } sub _jcidsysteminfo { my $self = shift; unless( $self->{jcidsysteminfo} ) { $self->{jcidsysteminfo} = $self->indirect(dictionary({ Registry => 'Adobe', Ordering => 'Japan1', Supplement => 2, })); } $self->{jcidsysteminfo}; } sub _jfontdescriptor { my($self, $basefont) = @_; unless( $self->{jfontdescriptor}->{$basefont} ) { $self->{jfontdescriptor}->{$basefont} = $self->indirect(dictionary($PDFJ::Default{JFD}{$basefont})); } $self->{jfontdescriptor}->{$basefont}; } sub _nextimagenum { my $self = shift; 1 + scalar keys %{$self->{imagelist}}; } sub _registimage { my($self, $name, $image) = @_; $self->{imagelist}->{$name} = $image; } #-------------------------------------------------------------------------- package PDFJ::Font; use strict; #-------------------------------------------------------------------------- package PDFJ::AFont; use strict; use Carp; use SelfLoader; use PDFJ::Object; use vars qw(@ISA); @ISA = qw(PDFJ::Font); sub new_std { my($class, $docobj, $basefont, $encoding) = @_; croak "illegal ascii font name: $basefont" unless $PDFJ::Default{Fonts}{$basefont} eq 'a'; my $name = $docobj->_fontname($basefont, $encoding); return $docobj->_fontobj($name) if $name; $name = "F".$docobj->_nextfontnum; my $font = $docobj->indirect(dictionary({ Type => name('Font'), Name => name($name), BaseFont => name($basefont), Subtype => name('Type1'), Encoding => name($encoding), })); my $width = fontwidth($basefont); my $self = bless {docobj => $docobj, basefont => $basefont, encoding => $encoding, font => $font, name => $name, width => $width, direction => 'H'}, $class; $docobj->_registfont($self); $self; } sub new_ttf { my($class, $docobj, $ttffile, $encoding) = @_; my $name = $docobj->_fontname($ttffile, $encoding); return $docobj->_fontobj($name) if $name; $name = "F".$docobj->_nextfontnum; my $ttf = PDFJ::Util::ttfopen($ttffile); my $info = $ttf->pdf_info_ascii($encoding); croak "'$ttffile' embedding inhibited" if $info->{EmbedFlag} == 2 || $info->{EmbedFlag} & 0x200; my $size = -s $ttffile; my($encoded, $filter) = $docobj->_makestream($ttffile); my $basefont = $info->{BaseFont}; my @widths = @{$info->{Widths}}; my $font = $docobj->indirect(dictionary({ Type => name('Font'), Name => name($name), BaseFont => name($basefont), Subtype => name('TrueType'), Encoding => name($info->{Encoding}), FirstChar => $info->{FirstChar}, LastChar => $info->{LastChar}, Widths => $docobj->indirect(array(\@widths)), FontDescriptor => $docobj->indirect(dictionary({ Type => name('FontDescriptor'), Ascent => $info->{Ascent}, CapHeight => $info->{CapHeight}, Descent => $info->{Descent}, Flags => $info->{Flags}, FontBBox => $info->{FontBBox}, FontName => name($info->{FontName}), ItalicAngle => $info->{ItalicAngle}, StemV => 0, # OK? FontFile2 => $docobj->indirect(stream(dictionary => { Filter => $filter, Length => length($encoded), Length1 => $size, }, stream => $encoded)), })), })); my $self = bless {docobj => $docobj, # basefont => $basefont, encoding => $encoding, ttffile => $ttffile, font => $font, name => $name, width => $info->{Widths}, direction => 'H'}, $class; $docobj->_registfont($self); $self; } sub selectname { my($self, $style) = @_; my $docobj = $self->{docobj}; $docobj->_bolditalicname($self->{name}, $style); } sub hash { my $self = shift; ($self->{name}, $self->{font}); } sub string_fontwidth { my($self, $string) = @_; my $fontwidth = $self->{width}; my $width = 0; for my $c(split '', $string) { $width += $fontwidth->[ord $c]; } $width / 1000; } sub astring_fontwidth { &string_fontwidth; } # NOT method my %FontWidth; sub fontwidth { my($basefont) = @_; $basefont =~ s/-/_/g; return $FontWidth{$basefont} if $FontWidth{$basefont}; my $func = "fontwidth_$basefont"; my $result = eval { no strict 'refs'; &$func(); }; croak $@ if $@; $FontWidth{$basefont} = $result if $result; $result; } #-------------------------------------------------------------------------- package PDFJ::JFont; use strict; use Carp; use PDFJ::Object; use vars qw(@ISA); @ISA = qw(PDFJ::Font); sub new_std { my($class, $docobj, $basefont, $encoding, $hfontobj) = @_; croak "illegal japanese font name: $basefont" unless $PDFJ::Default{Fonts}{$basefont} eq 'j'; croak "ascii font type mismatch" if $hfontobj && !UNIVERSAL::isa($hfontobj, "PDFJ::AFont"); my $name = $docobj->_fontname($basefont, $encoding); my $hname = $hfontobj ? $hfontobj->{name} : undef; return $docobj->_fontobj($name, $hname) if $name && $docobj->_fontobj($name, $hname); # Zenkaku font my $code = $PDFJ::Default{Jcode}; my($direction) = $encoding =~ /-(\w+)$/; my($zname, $zfont); if( $name ) { $zname = $name; $zfont = $docobj->_font($name); } else { my $jcidsi = $docobj->_jcidsysteminfo; my $jfd = $docobj->_jfontdescriptor($basefont); $zname = "F".$docobj->_nextfontnum; $zfont = $docobj->indirect(dictionary({ Name => name($zname), Type => name("Font"), Subtype => name('Type0'), Encoding => name($encoding), BaseFont => name("$basefont-$encoding"), DescendantFonts => [{ Type => name('Font'), Subtype => name('CIDFontType0'), BaseFont => name($basefont), CIDSystemInfo => $jcidsi, DW => 1000, W => [231, 389, 500, 631, [500]], FontDescriptor => $jfd, }], })); } # Hankaku font my($combo, $hfont); if( $hfontobj ) { $combo = 1; $hname = $hfontobj->{name}; $hfont = $hfontobj->{font}; } else { $combo = 0; $hname = $zname; $hfont = $zfont; } my $self = bless { docobj => $docobj, basefont => $basefont, encoding => $encoding, zfont => $zfont, hfont => $hfont, zname => $zname, hname => $hname, direction => $direction, code => $code, combo => $combo, hfontobj => $hfontobj, }, $class; $docobj->_registfont($self); $self; } sub new_ttf { my($class, $docobj, $ttffile, $encoding, $hfontobj) = @_; croak "TrueType subset embedding requires PDF version 1.3 or above" if $docobj->{version} < 1.3; croak "ascii font type mismatch" if $hfontobj && !UNIVERSAL::isa($hfontobj, "PDFJ::AFont"); my $name = $docobj->_fontname($ttffile, $encoding); my $hname = $hfontobj ? $hfontobj->{name} : undef; return $docobj->_fontobj($name, $hname) if $name && $docobj->_fontobj($name, $hname); # Zenkaku font my $code = $PDFJ::Default{Jcode}; my($direction) = $encoding =~ /-(\w+)$/; my($zname, $zfont); if( $name ) { $zname = $name; $zfont = $docobj->_font($name); } else { my $ttf = PDFJ::Util::ttfopen($ttffile); my $info = $ttf->pdf_info_japan($encoding); croak "'$ttffile' embedding inhibited ($info->{EmbedFlag})" if $info->{EmbedFlag} == 2 || $info->{EmbedFlag} & 0x100 || $info->{EmbedFlag} & 0x200; my $subsetname = $docobj->_nextsubsettag . '+' . $info->{BaseFont}; my $basefont = $info->{BaseFont}; my $jcidsi = $docobj->_jcidsysteminfo; $zname = "F".$docobj->_nextfontnum; $zfont = $docobj->indirect(dictionary({ Name => name($zname), Type => name("Font"), Subtype => name('Type0'), Encoding => name($encoding), BaseFont => name($subsetname), DescendantFonts => [$docobj->indirect(dictionary({ Type => name('Font'), Subtype => name('CIDFontType2'), # TrueType BaseFont => name($basefont), CIDSystemInfo => $jcidsi, DW => 1000, W => [231, 389, 500, 631, [500]], FontDescriptor => $docobj->indirect(dictionary({ Type => name('FontDescriptor'), Ascent => $info->{Ascent}, CapHeight => $info->{CapHeight}, Descent => $info->{Descent}, Flags => $info->{Flags}, FontBBox => $info->{FontBBox}, FontName => name($subsetname), ItalicAngle => $info->{ItalicAngle}, StemV => 0, # OK? # FontFile2 added later })), # CIDToGIDMap added later }))], })); $docobj->_registsubset( name => $zname, ttf => $ttf, direction => $direction); } my $subset_unicodes = $docobj->_subsetttf($zname)->{subset_unicodes}; # Hankaku font my($combo, $hfont); if( $hfontobj ) { $combo = 1; $hname = $hfontobj->{name}; $hfont = $hfontobj->{font}; } else { $combo = 0; $hname = $zname; $hfont = $zfont; } my $self = bless { docobj => $docobj, #basefont => $basefont, encoding => $encoding, ttffile => $ttffile, zfont => $zfont, hfont => $hfont, zname => $zname, hname => $hname, direction => $direction, code => $code, combo => $combo, hfontobj => $hfontobj, subset_unicodes => $subset_unicodes, }, $class; $docobj->_registfont($self); $self; } sub selectname { my($self, $style, $mode) = @_; my $docobj = $self->{docobj}; if( $self->{combo} ) { split $;, $docobj->_bolditalicname( join($;, $self->{zname}, $self->{hname}), $style); } else { $docobj->_bolditalicname($self->{zname}, $style); } } sub hash { my $self = shift; ($self->{zname}, $self->{zfont}, $self->{hname}, $self->{hfont}); } sub astring_fontwidth { my($self, $string) = @_; my $combo = $self->{combo}; my $hfont = $self->{hfontobj}; if( $combo ) { $hfont->string_fontwidth($string); } else { length($string) / 2; } } #-------------------------------------------------------------------------- package PDFJ::BlockElement; use Carp; use strict; sub size { 0 } sub preskip { 0 } sub postskip { 0 } sub postnobreak { 0 } sub breakable { 0 } sub float { "" } #-------------------------------------------------------------------------- package PDFJ::Showable; use Carp; use strict; use PDFJ::Object; use vars qw(@ISA); @ISA = qw(PDFJ::BlockElement); sub show { my($self, $page, $x, $y, $align, $transtype, @args) = PDFJ::Util::methodargs([qw(page x y align transtype transargs)], @_); if( ref($args[0]) eq 'ARRAY' ) { @args = @{$args[0]}; } if( $transtype ) { if( $transtype eq 'magnify' ) { my($xmag, $ymag) = @args; $page->addcontents("q", numbers($xmag, 0, 0, $ymag, $x, $y), "cm"); } elsif( $transtype eq 'rotate' ) { my($rad) = @args; my $sin = sin($rad); my $msin = -$sin; my $cos = cos($rad); $page->addcontents("q", numbers($cos, $sin, $msin, $cos, $x, $y), "cm"); } elsif( $transtype eq 'distort' ) { my($xtan, $ytan) = @args; $page->addcontents("q", numbers(1, $xtan, $ytan, 1, $x, $y), "cm"); } else { croak "unknown transformation type"; } ($x, $y) = (0, 0); } if( $align ) { if( $align =~ /l/ ) { $x -= $self->left; } elsif( $align =~ /r/ ) { $x -= $self->right; } elsif( $align =~ /c/ ) { $x -= ($self->left + $self->right) / 2; } if( $align =~ /t/ ) { $y -= $self->top; } elsif( $align =~ /b/ ) { $y -= $self->bottom; } elsif( $align =~ /m/ ) { $y -= ($self->top + $self->bottom) / 2; } } $self->_show($page, $x, $y); if( $transtype ) { $page->addcontents("Q"); } return; } #-------------------------------------------------------------------------- package PDFJ::Style; use strict; use Carp; sub new { my($class, @args) = @_; if( ref($class) ) { $class = ref($class); } my $self; if( @args == 1 ) { if( $args[0] && !ref($args[0]) ) { $self = PDFJ::Util::strstyle2hashref($args[0]); } elsif( $args[0] && (ref($args[0]) eq 'HASH' || ref($args[0]) eq $class) ) { %$self = %{$args[0]}; } else { croak "illegal $class->new() argument"; } } else { %$self = @args; } if( keys %$self == 1 && $self->{style} ) { $self = $class->new($self->{style}); } for my $key(keys %$self) { if( $key =~ /(style|color)$/ && (!ref($self->{$key}) || ref($self->{$key}) eq 'HASH') ) { $self->{$key} = $key =~ /color$/ ? PDFJ::Color->new($self->{$key}) : $key eq 'withnotestyle' ? PDFJ::TextStyle->new($self->{$key}) : $key =~ /style$/ ? PDFJ::ShapeStyle->new($self->{$key}) : $self->{$key}; } } bless $self, $class; } sub clone { my($self, @args) = @_; my $clone; if( @args ) { my %args; if( @args == 1 ) { if( !ref($args[0]) ) { %args = %{PDFJ::Util::strstyle2hashref($args[0])}; } elsif( ref($args[0]) eq 'HASH' || ref($args[0]) eq ref($self) ) { %args = %{$args[0]}; } else { croak "illegal clone() argument"; } } else { %args = @args; } if( keys %args == 1 && $args{style} ) { $clone = $self->clone($args{style}); } else { $clone = $self->new(%$self); for my $key(keys %args) { $clone->{$key} = $args{$key}; } } } else { $clone = $self->new(%$self); } $clone; } sub merge { my($self, $from) = @_; for my $key(keys %$from) { $self->{$key} = $from->{$key} unless exists $self->{$key}; } $self; } #-------------------------------------------------------------------------- package PDFJ::TextStyle; use strict; use Carp; use vars qw(@ISA); @ISA = qw(PDFJ::Style); sub TStyle { PDFJ::TextStyle->new(@_) } sub merge { my($self, $from) = @_; if( $self->{suffix} ) { $self->{fontsize} = $from->{fontsize} * $PDFJ::Default{SuffixSize}; $self->{rise} = $self->{suffix} eq 'u' ? $from->{fontsize} * $PDFJ::Default{USuffixRise} : $self->{suffix} eq 'l' ? $from->{fontsize} * $PDFJ::Default{LSuffixRise} : 0; } $self->SUPER::merge($from); } sub selectfontname { my($self, $mode) = @_; my $font = $self->{font} or return; $font->selectname($self, $mode); } #-------------------------------------------------------------------------- package PDFJ::TextSpec; use Carp; use strict; use PDFJ::Object; sub new { my($class, @args) = @_; my $self = bless {}, $class; $self->set(@args) if @args; $self; } # for debug sub print { my($self) = @_; for my $key(qw(fontsize render rise mode)) { print "$key => $self->{$key}, "; } print "\n"; } sub set { my($self, $style, $fontname) = @_; %$self = (); for my $key(qw(fontsize render rise shapestyle)) { if( exists $style->{$key} ) { $self->{$key} = $style->{$key}; } } $self->{fontname} = $fontname; } sub copy { my($self, $from) = @_; %$self = %$from; } sub equal { my($self, $other) = @_; for my $key(qw(fontname fontsize render rise shapestyle)) { return 0 if ($self->{$key} || "") ne ($other->{$key} || ""); } return 1; } sub pdf { my($self) = @_; croak "no fontsize specification" unless $self->{fontsize}; my $fontname = $self->{fontname}; my $fontsize = $self->{fontsize}; my $rise = $self->{rise} || 0; my $render = $self->{render} || 0; my @shapepdf = $self->{shapestyle} ? $self->{shapestyle}->pdf : (); my @pdf = ("q"); push @pdf, @shapepdf if @shapepdf; push @pdf, "BT /$fontname", number($fontsize), "Tf", number($rise), "Ts $render Tr"; @pdf; } #-------------------------------------------------------------------------- package PDFJ::NewLine; use Carp; use strict; sub NewLine { PDFJ::NewLine->new(@_) } sub new { my($class) = @_; bless \$class, $class; } #-------------------------------------------------------------------------- package PDFJ::Outline; use Carp; use strict; sub Outline { PDFJ::Outline->new(@_) } sub new { my($class, $title, $level) = PDFJ::Util::methodargs([qw(title level)], @_); bless {outlinetitle => $title, outlinelevel => $level}, $class; } #-------------------------------------------------------------------------- package PDFJ::Dest; use Carp; use strict; sub Dest { PDFJ::Dest->new(@_) } sub new { my($class, $name) = PDFJ::Util::methodargs([qw(name)], @_); bless {destname => $name}, $class; } #-------------------------------------------------------------------------- package PDFJ::Text; use Carp; use strict; use PDFJ::Object; use vars qw(@ISA); @ISA = qw(PDFJ::Showable); sub Text { PDFJ::Text->new(@_) } sub new { my($class, $texts, $style) = PDFJ::Util::listargs2ref(1, 1, PDFJ::Util::methodargs([qw(texts style)], @_)); if( UNIVERSAL::isa($style, 'PDFJ::TextStyle') ) { $style = $style->clone; } elsif( $style && (!ref($style) || ref($style) eq 'HASH') ) { $style = PDFJ::TextStyle->new($style); } else { croak "style argument must be a PDFJ::TextStyle object or HASHref"; } my $self = bless { texts => $texts, style => $style }, $class; $self->mergestyle; # $self->print; $self->makechunks; $self->makerubytext; $self; } sub mergestyle { my($self) = @_; my $style = $self->style; return unless $style->{font}; for my $text(@{$self->texts}) { if( UNIVERSAL::isa($text, 'PDFJ::Text') ) { $text->style->merge($style); $text->mergestyle; } } } # for debug sub print { my($self, $indent) = @_; my $style = $self->style; print $indent,join(',',%$style),"\n"; for my $text(@{$self->texts}) { if( UNIVERSAL::isa($text, 'PDFJ::Text') ) { $text->print("$indent "); } else { print "$indent\[$text]\n"; } } } sub makechunks { my($self) = @_; my $style = $self->style; return unless $style->{font}; $self->{chunks} = []; $self->{lines} = []; for my $text(@{$self->texts}) { if( UNIVERSAL::isa($text, 'PDFJ::Text') ) { $text->makechunks unless $text->chunks; $self->catchunks($text->chunks); } elsif( UNIVERSAL::isa($text, 'PDFJ::Showable') ) { $self->catchunks([_objchunk($text, $self->style)]); # } elsif( UNIVERSAL::isa($text, 'PDFJ::Image') ) { # $self->catchunks([_imagechunk($text, $self->style)]); # } elsif( UNIVERSAL::isa($text, 'PDFJ::Shape') ) { # $self->catchunks([_shapechunk($text, $self->style)]); } elsif( UNIVERSAL::isa($text, 'PDFJ::NewLine') ) { $self->catchunks([_newlinechunk($self->style)]); } elsif( UNIVERSAL::isa($text, 'PDFJ::Outline') ) { $self->catchunks([_outlinechunk($text, $self->style)]); } elsif( UNIVERSAL::isa($text, 'PDFJ::Dest') ) { $self->catchunks([_destchunk($text, $self->style)]); } elsif( UNIVERSAL::isa($text, 'PDFJ::TextChunk') ) { $self->catchunks([$text]); } else { $self->catchunks($self->splittext($text)); } } } sub makerubytext { my($self) = @_; return unless $self->style->{font}; for my $chunk(@{$self->chunks}) { my $style = $chunk->{Style}; if( $style->{ruby} ) { croak "ruby class mismatch" unless $chunk->{Class} == 11 || $chunk->{Class} == 17; my $rubystyle = $style->clone; delete $rubystyle->{ruby}; delete $rubystyle->{withbox}; delete $rubystyle->{withline}; $rubystyle->{fontsize} /= 2; my $rubytext = PDFJ::Text->new($style->{ruby}, $rubystyle); my $rubysize = $rubytext->size; my $chunksize = _chunksize($chunk, 1); if( $rubysize < $chunksize ) { my $alt = PDFJ::Paragraph->new($rubytext, PDFJ::ParagraphStyle->new(size => $chunksize, align => 'ruby', linefeed => 0)); $chunk->{RubyText} = $alt; } elsif( $rubysize > $chunksize ) { my $altstyle = $style->clone; delete $altstyle->{ruby}; delete $altstyle->{withbox}; delete $altstyle->{withline}; my $altchunk = $chunk->clone; $altchunk->{Style} = $altstyle; my $alt = PDFJ::Paragraph->new( PDFJ::Text->new($altchunk, $altstyle), PDFJ::ParagraphStyle->new(size => $rubysize, align => 'ruby', linefeed => 0)); $chunk->{AltObj} = $alt; $chunk->{RubyText} = $rubytext; } else { $chunk->{RubyText} = $rubytext; } } } } sub texts { $_[0]->{texts} } sub chunks { $_[0]->{chunks} } sub chunksnum { scalar(@{$_[0]->{chunks}}) } sub chunk { $_[0]->{chunks}[$_[1]] } sub style { $_[0]->{style} } sub direction { $_[0]->{style}{font}{direction} } sub fontsize { $_[0]->{style}{fontsize} } sub width { my($self) = @_; $self->direction eq 'H' ? _chunkssize($self->{chunks}) : $self->fontsize; } sub height { my($self) = @_; $self->direction eq 'H' ? $self->fontsize : _chunkssize($self->{chunks}); } sub left { my($self) = @_; $self->direction eq 'H' ? 0 : - ($self->fontsize / 2); } sub right { my($self) = @_; $self->direction eq 'H' ? _chunkssize($self->{chunks}) : $self->fontsize / 2; } sub top { my($self) = @_; $self->direction eq 'H' ? $self->fontsize * (1 - $PDFJ::Default{HBaseShift}) : 0; } sub bottom { my($self) = @_; $self->direction eq 'H' ? $self->fontsize * (- $PDFJ::Default{HBaseShift}) : _chunkssize($self->{chunks}); } sub size { my($self, $direction) = @_; # neglect $direction _chunkssize($self->{chunks}); } sub fixsize { my($self, $start, $count, $fixedglues) = @_; _chunksfixsize($self->{chunks}, $start, $count, $fixedglues); } sub count { my($self) = @_; _chunkscount($self->{chunks}); } sub dehyphen { my($self) = @_; return unless $self->{hyphened}; my $chunks = $self->chunks; for( my $j = 0; $j < @$chunks; $j++ ) { my $chunk = $chunks->[$j]; my $hyphened = $chunk->{Hyphened}; if( $hyphened && $j < @$chunks - 1 ) { $chunk->{String} =~ s/-$// if $hyphened == 2; $chunk->{String} .= $chunks->[$j + 1]->{String}; $chunk->{Count} = length $chunk->{String}; $chunk->{Hyphened} = 0; splice @$chunks, $j + 1, 1; } } $self->{hyphened} = 0; } sub _fold { my($self, $linesize, $align) = @_; my $chunks = $self->chunks; return unless @$chunks; $self->dehyphen; my @lines; my @linesizes = ref($linesize) eq 'ARRAY' ? @$linesize : ($linesize); my $lastlinesize = $linesizes[$#linesizes]; my $rubyshift; if( $align eq 'ruby' ) { $rubyshift = ($linesizes[0] - _chunkssize($chunks)) / $self->count; $linesizes[0] -= $rubyshift; $rubyshift /= 2; $align = 'W'; } my $start = 0; while( $start < @$chunks ) { $linesize = @linesizes ? shift(@linesizes) : $lastlinesize; croak "not enough paragraph size" if $linesize < 0; my $size = 0; my $decsize = 0; my $foldpos = $start; my $canpos = $start; my $forced; for( my $j = $start; $j < @$chunks; $j++ ) { my $chunk = $chunks->[$j]; if( $chunk->{Splittable} == 2 ) { $foldpos = $j + 1; $forced = 1; last; } my($chunksize, $decchunksize) = _chunksize($chunk, ($j == $start)); $size += $chunksize; $decsize += $decchunksize unless $j == $start; my $k = $j + 1; if( $k == @$chunks || ($chunks->[$k]{Splittable} && !_isnoeol($chunks, $k) && !_isnobol($chunks, $k)) ) { if( $size == $linesize ) { $foldpos = $k; last; } elsif( $size < $linesize ) { $canpos = $k; } else { # $size > $linesize my $hyphenpos = 0; if( $align =~ /w/i && $size - $decsize <= $linesize ) { $foldpos = $k; } elsif( ($hyphenpos = _hyphenpos($chunks->[$j], $size - $linesize)) ) { _inshyphen($chunks, $j, $hyphenpos); $self->{hyphened} = 1; $foldpos = $k; } elsif( $k == $start + 1 ) { $foldpos = $k; } else { $foldpos = $canpos; } last; } } #print "$j:$start:($chunk->{Splittable})[$chunk->{String}] $chunksize(-$decchunksize) $size(-$decsize) $canpos\n"; } if( $foldpos == $start && $size > $linesize && !($align =~ /w/i && $size - $decsize <= $linesize) ) { $foldpos = $canpos; } $foldpos = @$chunks if $foldpos == $start; my $nextpos = $foldpos; unless( $forced ) { while( $nextpos < @$chunks && $chunks->[$nextpos]{Class} eq 16 ) { $nextpos++; } } while( $foldpos > 0 && ($chunks->[$foldpos - 1]{Class} eq 16 || $chunks->[$foldpos - 1]{Splittable} == 2) ) { $foldpos--; } my $count = $foldpos - $start; $size = _chunkssize($chunks, $start, $count); my $shift = 0; my $fixedglues = []; if( $align eq 'e' ) { $shift = $linesize - $size; } elsif( $align eq 'm' ) { $shift = ($linesize - $size) / 2; } elsif( $align eq 'W' || ($align eq 'w' && $count && (($nextpos < @$chunks && $chunks->[$foldpos]{Splittable} != 2) || $size > $linesize)) ) { $fixedglues = $self->fixglue($start, $count, $linesize - $size); if( $rubyshift ) { $shift = $rubyshift + ($linesize - $self->fixsize($start, $count, $fixedglues)) / 2; } } my %hash =( Start => $start , Count => $count , Shift => $shift , FixedGlues => $fixedglues , ); push(@lines,\%hash); $start = $nextpos; } @lines; } sub _hyphenpos { my($chunk, $decsize) = @_; return unless $chunk->{Class} == 17 && !$chunk->{Style}{nohyphen} && !$chunk->{Style}{ruby}; my $string = $chunk->{String}; #print "$string, $decsize\n"; my($can, $canleft, $pre, $word); if( $string =~ /([A-Za-z]-)([A-Za-z])/ ) { $can = $`.$1; $canleft = $2.$'; #' } elsif( $string =~ /[A-Za-z]{5,}/ ) { $pre = $`; $word = $&; } return unless $can || $word; my $fontobj = $chunk->{Style}{font}; my $fontsize = $chunk->{Style}{fontsize}; $decsize /= $fontsize; if( $can ) { if( $fontobj->astring_fontwidth($canleft) >= $decsize ) { return length($can); } else { return; } } my $size = $fontobj->astring_fontwidth($word); return if $size <= $decsize; my $maxsize = $size - $decsize; for my $pos(reverse PDFJ::Util::hyphenate($word)) { return length($pre) + $pos if $fontobj->astring_fontwidth($pre.substr($word, 0, $pos).'-') <= $maxsize; } return; } sub _inshyphen { my($chunks, $idx, $hyphenpos) = @_; my $chunk = $chunks->[$idx]; my $string = $chunk->{String}; my $inschunk; @$inschunk = @$chunk; my $work = substr $string, 0, $hyphenpos; if( $work =~ /-$/ ) { $chunk->{Hyphened} = 1; } else { $chunk->{Hyphened} = 2; $work .= '-'; } $chunk->{String} = $work; $chunk->{Count} = length $chunk->{String}; $inschunk->{String} = substr $string, $hyphenpos; $inschunk->{Count} = length $inschunk->{String}; splice @$chunks, $idx + 1, 0, $inschunk; } sub _chunkssize { my($chunks, $start, $count) = @_; die "cannot calc size of Text, may be missing font in TextStyle" unless $chunks; $start += 0; $start = 0 if $start < 0; $count ||= @$chunks - $start; my $result; for(my $j = 0; $j < $count && $start + $j < @$chunks; $j++ ) { $result += _chunksize($chunks->[$start + $j], ($j == 0)); } $result; } sub _chunksfixsize { my($chunks, $start, $count, $fixedglues) = @_; $start += 0; $start = 0 if $start < 0; $count ||= @$chunks - $start; my $result; for(my $j = 0; $j < $count && $start + $j < @$chunks; $j++ ) { $result += _chunkfixsize($chunks->[$start + $j], $fixedglues->[$j]); } $result; } sub _chunkscount { my($chunks, $start, $count) = @_; $start += 0; $start = 0 if $start < 0; $count ||= @$chunks - $start; my $result; for(my $j = 0; $j < $count && $start + $j < @$chunks; $j++ ) { $result += _chunkcount($chunks->[$start + $j]); } $result; } # check if last chunk is NoEOL sub _isnoeol { my($chunks, $pos) = @_; while( $pos > 0 && $chunks->[$pos - 1]{Class} == 16 ) { $pos--; } return unless $pos > 0; $PDFJ::Default{NoEOL}[$chunks->[$pos - 1]{Class}]; } # check if next chunk is NoBOL sub _isnobol { my($chunks, $pos) = @_; while( $pos < @$chunks && $chunks->[$pos]{Class} == 16 ) { $pos++; } return unless $pos < @$chunks; $PDFJ::Default{NoBOL}[$chunks->[$pos]{Class}]; } sub _chunkcount { my($chunk) = @_; $chunk->{Count}; } sub _chunksize { my($chunk, $noglue) = @_; my $fontobj = $chunk->{Style}{font}; my $fontsize = $chunk->{Style}{fontsize}; my $direction = $fontobj->{direction}; my $size = $direction eq 'H' ? _chunkfontsizeH($fontobj, $fontsize, $chunk) : _chunkfontsizeV($fontobj, $fontsize, $chunk); $size += $fontsize * (($noglue ? 0 : $chunk->{Glue}) - $chunk->{PreShift} - $chunk->{PostShift}); if( wantarray ) { my $decsize = $chunk->{GlueDec} * $fontsize; my $incsize = $chunk->{GlueInc} * $fontsize; ($size, $decsize, $incsize); } else { $size; } } sub _chunkfixsize { my($chunk, $fixedglue) = @_; $fixedglue ||= 0; my $fontobj = $chunk->{Style}{font}; my $fontsize = $chunk->{Style}{fontsize}; my $direction = $fontobj->{direction}; my $size = $direction eq 'H' ? _chunkfontsizeH($fontobj, $fontsize, $chunk) : _chunkfontsizeV($fontobj, $fontsize, $chunk); $size += $fontsize * ($fixedglue - $chunk->{PreShift} - $chunk->{PostShift}); $size; } sub _chunkfontsizeH { my($fontobj, $fontsize, $chunk) = @_; return $chunk->{AltObj}->size('H') if $chunk->{AltObj}; my $size = 0; if( UNIVERSAL::isa($fontobj, "PDFJ::JFont") ) { my $combo = $fontobj->{combo}; my $hfont = $fontobj->{hfontobj}; my $mode = $chunk->{Mode}; if( $mode eq 'z' ) { $size = $chunk->{Count}; } elsif( $mode eq 'h' ) { $size = $chunk->{Count} / 2; } elsif( $combo ) { $size = $hfont->string_fontwidth($chunk->{String}); } else { $size = $chunk->{Count} / 2; } } elsif( UNIVERSAL::isa($fontobj, "PDFJ::AFont") ) { $size = $fontobj->string_fontwidth($chunk->{String}); } else { croak "internal error: missing font object"; } $size *= $fontsize; $size; } sub _chunkfontsizeV { my($fontobj, $fontsize, $chunk) = @_; return $chunk->{AltObj}->size('V') if $chunk->{AltObj}; my $size = 0; if( UNIVERSAL::isa($fontobj, "PDFJ::JFont") ) { my $combo = $fontobj->{combo}; my $hfont = $fontobj->{hfontobj}; my $mode = $chunk->{Mode}; if( $mode eq 'z' ) { $size = $chunk->{Count}; } elsif( $mode eq 'h' ) { $size = $chunk->{Count}; } elsif( $combo ) { $size = $chunk->{Class} == 11 ? 1 : $hfont->string_fontwidth($chunk->{String}); } else { $size = $chunk->{Count}; } } elsif( UNIVERSAL::isa($fontobj, "PDFJ::AFont") ) { $size = $fontobj->string_fontwidth($chunk->{String}); } else { croak "internal error: missing font object"; } $size *= $fontsize; $size; } sub fixglue { my($self, $start, $count, $incsize) = @_; return unless $incsize; if( $incsize > 0 ) { &fixglueinc; } else { &fixgluedec; } } sub fixglueinc { my($self, $start, $count, $incsize) = @_; my @fixedglues; my %incgluesum; my $chunksnum = $self->chunksnum; # start counter is not 0 but 1 because first chunk glue is not used for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); if( $chunk->{GlueInc} ) { $incgluesum{$chunk->{GluePref} + 0} += $chunk->{GlueInc} * $chunk->{Style}{fontsize}; } } for my $pref(reverse sort keys %incgluesum) { last if $incsize <= 0; my $incgluesum = $incgluesum{$pref}; my $ratio = $incgluesum > $incsize ? $incsize / $incgluesum : 1; for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); if( $chunk->{GlueInc} && $chunk->{GluePref} == $pref ) { #$chunk->{GlueFix} = $chunk->{GlueInc} * $ratio; $fixedglues[$j] = $chunk->{GlueInc} * $ratio; } } #$incsize -= $incgluesum * $ratio; $incsize -= $incgluesum; } if( $incsize > 0 ) { my $splittables = 0; for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { $splittables++ if $self->chunk($start + $j)->{Splittable}; } if( $splittables ) { for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); if( $chunk->{Splittable} ) { #$chunk->{GlueFix} += $incsize / $chunk->{Style}{fontsize} / # $splittables; $fixedglues[$j] += $incsize / $chunk->{Style}{fontsize} / $splittables; } } } } \@fixedglues; } sub fixgluedec { my($self, $start, $count, $incsize) = @_; my $decsize = -$incsize; my @fixedglues; my %decgluesum; my $chunksnum = $self->chunksnum; # start counter is not 0 but 1 because first chunk glue is not used for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); if( $chunk->{GlueDec} ) { $decgluesum{$chunk->{GluePref} + 0} += $chunk->{GlueDec} * $chunk->{Style}{fontsize}; } } for my $pref(reverse sort keys %decgluesum) { last if $decsize <= 0; my $decgluesum = $decgluesum{$pref}; my $ratio = $decgluesum > $decsize ? $decsize / $decgluesum : 1; for( my $j = 1; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); if( $chunk->{GlueDec} && $chunk->{GluePref} == $pref ) { #$chunk->{GlueFix} = -$chunk->{GlueDec} * $ratio; $fixedglues[$j] = -$chunk->{GlueDec} * $ratio; } } #$decsize -= $decgluesum * $ratio; $decsize -= $decgluesum; } \@fixedglues; } sub _show { my($self, $page, $x, $y) = @_; $self->_showpart($page, $x, $y, 0, $self->chunksnum); } # This mega subroutine is too complex and patchy, needs refactering! sub _showpart { my($self, $page, $x, $y, $start, $count, $fixedglues) = @_; my $docobj = $page->docobj; my $chunksnum = $self->chunksnum; my %usefontname; my(@tj, @dotpdf, @shapepdf); my $lasttextspec = PDFJ::TextSpec->new; my($ulx, $uly, $bxx, $bxy); my($lastfontsize, $postshift, $slant, $lastfrx, $lastfry, $va) = (0) x 6; my($lastfontname, $withlinestyle, $withbox, $withboxstyle) = ("") x 4; for( my $j = 0; $j < $count && $start + $j < $chunksnum; $j++ ) { my $chunk = $self->chunk($start + $j); my $mode = $chunk->{Mode}; my $class = $chunk->{Class}; # my $style = $chunk->{Style}; my $style = $chunk->{Style}->clone; my $direction = $style->{font}->{direction}; my($fontname, $hname) = $style->selectfontname($mode); my $fontobj = $style->{font} = $docobj->_fontobj($fontname, $hname); my $combo = $fontobj->{combo}; my $usefname = $mode eq 'a' && $hname ? $hname : $fontname; $usefontname{$usefname}++; my $fontsize = $style->{fontsize}; $postshift *= $lastfontsize / $fontsize; $style->{slant} = 1 if $mode eq 'z' && $style->{italic}; my $textspec = PDFJ::TextSpec->new($style, $usefname); if( $direction eq 'V' && $combo && $mode eq 'a' ) { if( $va != $class ) { my($vax, $vay) = $class == 11 ? ($x - _chunkfontsizeH($fontobj, $fontsize, $chunk) / 2, $y - $fontsize * $PDFJ::Default{VHShift}) : ($x + $fontsize * $PDFJ::Default{VAShift}, $y); push @tj, "] TJ ET Q " if @tj; push @tj, ($class == 11 ? ($textspec->pdf, numbers($vax, $vay), "Td [") : ($textspec->pdf, numbers(0, -1, 1, 0, $vax, $vay), "Tm [")); $lasttextspec->copy($textspec); $va = $class; } } elsif( $va ) { push @tj, "] TJ ET Q " if @tj; push @tj, $textspec->pdf, numbers($x, $y), "Td ["; $lasttextspec->copy($textspec); $va = 0; } if( $style->{slant} ) { #croak "slant style for ascii not allowed" # if $mode eq 'a'; unless( $slant ) { my($sx, $sy) = $direction eq 'H' ? (0, $PDFJ::Default{SlantRatio}) : ($PDFJ::Default{SlantRatio}, 0); push @tj, "] TJ ET Q " if @tj; push @tj, $textspec->pdf, numbers(1, $sx, $sy, 1, $x, $y), "Tm ["; $lasttextspec->copy($textspec); $slant = 1; } } elsif( $slant ) { push @tj, "] TJ ET Q " if @tj; push @tj, $textspec->pdf, numbers($x, $y), "Td ["; $lasttextspec->copy($textspec); $slant = 0; } unless( $lasttextspec->equal($textspec) ) { push @tj, "] TJ ET Q " if @tj; push @tj, $textspec->pdf, numbers($x, $y), "Td ["; $lasttextspec->copy($textspec); } my $shift = $va == 11 ? 0 : $j == 0 ? $chunk->{PreShift} : $postshift + $chunk->{PreShift} - ($fixedglues ? $chunk->{Glue} + ($fixedglues->[$j] || 0): $chunk->{Glue}); $shift = -$shift if $direction eq 'V'; $shift *= 1000; if( $shift ) { my $vs = $va ? -$shift : $shift; push @tj, number($vs); } my($flx, $fly) = ($x, $y); my($frx, $fry) = ($x, $y); my($fcx, $fcy) = ($x, $y); if( $direction eq 'H' ) { $flx -= ($postshift - ($j == 0 ? 0 : ($fixedglues ? $chunk->{Glue} + ($fixedglues->[$j] || 0): $chunk->{Glue}))) * $fontsize; $frx = $flx + (_chunkfontsizeH($fontobj, $fontsize, $chunk) - ($chunk->{PreShift} + $chunk->{PostShift}) * $fontsize); $fcx = $flx + (_chunkfontsizeH($fontobj, $fontsize, $chunk) - ($chunk->{PreShift} + $chunk->{PostShift}) * $fontsize) / 2; } else { $fly += ($postshift - ($j == 0 ? 0 : ($fixedglues ? $chunk->{Glue} + ($fixedglues->[$j] || 0): $chunk->{Glue}))) * $fontsize; $fry = $fly - (_chunkfontsizeV($fontobj, $fontsize, $chunk) - ($chunk->{PreShift} + $chunk->{PostShift}) * $fontsize); $fcy = $fly - (_chunkfontsizeV($fontobj, $fontsize, $chunk) - ($chunk->{PreShift} + $chunk->{PostShift}) * $fontsize) / 2; } if( $mode eq 'O' || $mode eq 'D' ) { # Outline or Dest my($ox, $oy) = ($flx, $fly); if( $direction eq 'H' ) { $oy += (1 - $PDFJ::Default{HBaseShift}) * $fontsize; } else { $ox -= $fontsize / 2; } if( $mode eq 'O' ) { my $title = $style->{outlinetitle}; my $level = $style->{outlinelevel}; $docobj->add_outline($title, $page->dest('XYZ', $ox, $oy, 0), $level); } elsif( $mode eq 'D' ) { my $name = $style->{destname}; $docobj->add_dest($name, $page->dest('XYZ', $ox, $oy, 0)); } } if( $chunk->{AltObj} ) { my $altobj = $chunk->{AltObj}; my $altsize = $altobj->size($direction); my $objalign = $style->{objalign} || ""; my $align; my($asx, $asy) = (0, 0); if( $direction eq 'H' ) { if( $objalign =~ /t/ ) { $align = 'tl'; $asy += (1 - $PDFJ::Default{HBaseShift}) * $fontsize; } elsif( $objalign =~ /m/ ) { $align = 'ml'; $asy += (0.5 - $PDFJ::Default{HBaseShift}) * $fontsize; } else { # /b/ $align = 'bl'; $asy += (- $PDFJ::Default{HBaseShift}) * $fontsize; } } else { if( $objalign =~ /l/ ) { $align = 'tl'; $asx -= $fontsize / 2; } elsif( $objalign =~ /r/ ) { $align = 'tr'; $asx += $fontsize / 2; } else { # /c/ $align = 'tc'; } } $altsize = $altsize * 1000 / $fontsize; $altsize = -$altsize if $direction eq 'H'; push @tj, number($altsize); $altobj->show($page, $flx + $asx, $fly + $asy, $align); } else { my $ss = stringstring($chunk, $fontobj, $usefname); push @tj, $ss; } # } elsif( $chunk->{String} =~ /[^\x20-\x7e]/ ) { # my $ss = unpack('H*', $chunk->{String}); # $tj .= "<$ss> "; # } else { # my $ss = $chunk->{String}; # $ss =~ s/\\/\\\\/g; # $ss =~ s/\(/\\\(/g; # $ss =~ s/\)/\\\)/g; # $tj .= "($ss) "; # } $postshift = $chunk->{PostShift}; if( $style->{withline} ) { ($ulx, $uly) = ($flx, $fly) unless defined $ulx; $withlinestyle = $style->{withlinestyle}; if( $j == $count - 1 || $start + $j == $chunksnum - 1 ) { push @shapepdf, ($direction eq 'H' ? _withlinepdf($direction, $ulx, $uly, $frx - $ulx, $fontsize, $withlinestyle) : _withlinepdf($direction, $ulx, $uly, $fry - $uly, $fontsize, $withlinestyle)); } } elsif( defined $ulx ) { push @shapepdf, ($direction eq 'H' ? _withlinepdf($direction, $ulx, $uly, $lastfrx - $ulx, $lastfontsize, $withlinestyle) : _withlinepdf($direction, $ulx, $uly, $lastfry - $uly, $lastfontsize, $withlinestyle)); undef $ulx; undef $uly; } if( $style->{withbox} ) { ($bxx, $bxy) = ($flx, $fly) unless defined $bxx; $withbox = $style->{withbox}; $withboxstyle = $style->{withboxstyle}; if( $j == $count - 1 || $start + $j == $chunksnum - 1 ) { push @shapepdf, ($direction eq 'H' ? _withboxpdf($page, $direction, $bxx, $bxy, $frx - $bxx, $fontsize, $withbox, $withboxstyle) : _withboxpdf($page, $direction, $bxx, $bxy, $fry - $bxy, $fontsize, $withbox, $withboxstyle)); } } elsif( defined $bxx ) { push @shapepdf, ($direction eq 'H' ? _withboxpdf($page, $direction, $bxx, $bxy, $lastfrx - $bxx, $lastfontsize, $withbox, $withboxstyle) : _withboxpdf($page, $direction, $bxx, $bxy, $lastfry - $bxy, $lastfontsize, $withbox, $withboxstyle)); undef $bxx; undef $bxy; } if( $style->{withdot} ) { croak "withdot style needs JFont" unless UNIVERSAL::isa($fontobj, "PDFJ::JFont"); my($dx, $dy, $ds, $dcode); if( $direction eq 'H' ) { ($dx, $dy, $ds) = ( $fcx - $fontsize / 2 + $fontsize * $PDFJ::Default{HDotXShift}, $fcy + $fontsize * $PDFJ::Default{HDotYShift}, $fontsize); $dcode = unpack('H*', $PDFJ::Default{HDot}{$PDFJ::Default{Jcode}}) } else { ($dx, $dy, $ds) = ($fcx + $fontsize * $PDFJ::Default{VDotXShift}, $fcy + $fontsize / 2 + $fontsize * $PDFJ::Default{VDotYShift}, $fontsize); $dcode = unpack('H*', $PDFJ::Default{VDot}{$PDFJ::Default{Jcode}}) } my $fontname = $fontobj->{zname}; push @dotpdf, "BT 0 Ts 0 Tr /$fontname", number($ds), "Tf", numbers($dx, $dy), "Td <$dcode> Tj ET"; } if( $chunk->{RubyText} ) { my $rubytext = $chunk->{RubyText}; if( $direction eq 'H' ) { $rubytext->show($page, $flx, $fly + $PDFJ::Default{ORuby} * $fontsize / 1000); } else { $rubytext->show($page, $flx + $PDFJ::Default{RRuby} * $fontsize / 1000, $fly); } } if( $style->{withnote} ) { my $notetext = $style->{withnote}; if( !ref($notetext) && $style->{withnotestyle} ) { $notetext = PDFJ::Text->new($notetext, $style->{withnotestyle}); } my $notesize = $notetext->size; if( $direction eq 'H' ) { $notetext->show($page, $frx - $notesize, $fry + $PDFJ::Default{HNote} * $fontsize / 1000); } else { $notetext->show($page, $frx + $PDFJ::Default{VNote} * $fontsize / 1000, $fry + $notesize); } } ($lastfrx, $lastfry) = ($frx, $fry); $lastfontsize = $fontsize; if( $direction eq 'H' ) { $x += _chunkfontsizeH($fontobj, $fontsize, $chunk) - $shift * $fontsize / 1000; } else { $y -= _chunkfontsizeV($fontobj, $fontsize, $chunk) + $shift * $fontsize / 1000; } } push @tj, "] TJ ET Q " if @tj; # $tj =~ s/> <//g; # $tj =~ s/\) \(//g; $page->addcontents(@shapepdf); $page->addcontents(@tj); $page->addcontents(@dotpdf); $page->usefonts(keys %usefontname); } sub stringstring { my($chunk, $fontobj, $usefname) = @_; if( $chunk->{Mode} eq 'a' && $PDFJ::Default{Encodings}{$fontobj->{encoding}} eq 'ju' && $usefname eq $fontobj->{zname} ) { $chunk->{String} =~ s/./\x00$&/g; } my $result; if( $chunk->{String} =~ /[^\x20-\x7e]/ ) { my $ss = unpack('H*', $chunk->{String}); $result = "<$ss>"; } else { my $ss = $chunk->{String}; $ss =~ s/\\/\\\\/g; $ss =~ s/\(/\\\(/g; $ss =~ s/\)/\\\)/g; $result = "($ss)"; } $result; } sub _withlinepdf { my($direction, $x, $y, $w, $fontsize, $style) = @_; my $shape = PDFJ::Shape->new; if( $direction eq 'H' ) { $shape->textuline($x, $y, $w, $fontsize, $style); } else { $shape->textrline($x, $y, $w, $fontsize, $style); } $shape->pdf; } sub _withboxpdf { my($page, $direction, $x, $y, $w, $fontsize, $spec, $style) = @_; my $shape = PDFJ::Shape->new; $shape->textbox($direction, $x, $y, $w, $fontsize, $spec, $style); $shape->show_link($page); $shape->pdf; } # string splitting # character classes are # 0: begin paren # 1: end paren # 2: not at top of line # 3: ?! # 4: dot # 5: punc # 6: leader # 7: pre unit # 8: post unit # 9: zenkaku space # 10: hirakana # 11: japanese # 12: suffixed # 13: rubied # 14: number # 15: unit # 16: space # 17: ascii # modes are # 'z': zenkaku Japanese # 'h': hankaku Japanese # 'a': ascii # 'S': ShowableObj # 'N': Newline # 'O': Outline # 'D': Destination sub _specialchunk { my($style, $mode, $class, $splittable) = @_; PDFJ::TextChunk->new($style, $mode, $class, $splittable, 0, 0, 0, 0, 0, "", 0, 0); } sub _newlinechunk { my($textstyle) = @_; _specialchunk($textstyle, 'N', 11, 2); } sub _outlinechunk { my($outlineobj, $textstyle) = @_; my $style = $textstyle->clone(%$outlineobj); _specialchunk($style, 'O', 11, 1); } sub _destchunk { my($destobj, $textstyle) = @_; my $style = $textstyle->clone(%$destobj); _specialchunk($style, 'D', 11, 1); } sub _objchunk { my($obj, $textstyle) = @_; my $chunk = _specialchunk($textstyle, 'S', 11, 1); $chunk->{AltObj} = $obj; $chunk; } # obsolete sub _imagechunk { my($img, $textstyle) = @_; my $chunk = _specialchunk($textstyle, 'I', 11, 1); $chunk->{AltObj} = $img; $chunk; } # obsolete sub _shapechunk { my($shape, $textstyle) = @_; my $chunk = _specialchunk($textstyle, 'S', 11, 1); $chunk->{AltObj} = $shape; my($left, $bottom) = ($shape->left, $shape->bottom); $chunk; } sub catchunks { my($self, $src) = @_; my $dest = $self->chunks; if( @$dest && @$src ) { my($splittable, $glue, $gluedec, $glueinc, $gluepref); my $lastclass = _lastclass($dest); my $lastmode = _lastmode($dest); my $fchunk = $src->[0]; my $class = $fchunk->{Class}; my $mode = $fchunk->{Mode}; my $style = $fchunk->{Style}; $splittable = $fchunk->{Splittable} == 2 ? 2 : $style->{suffix} ? 0 : $lastmode eq 'O' ? 0 : $PDFJ::Default{Splittable}->[$lastclass][$class]; $glue = ($lastmode =~ /^[ON]$/ || $mode =~ /^[ON]$/) ? PDFJ::GlueNon : $PDFJ::Default{Glue}->[$lastclass][$class]; ($glue, $gluedec, $glueinc, $gluepref) = _calcglue($glue); $fchunk->{Splittable} = $splittable; $fchunk->{Glue} = $glue; $fchunk->{GlueDec} = $gluedec; $fchunk->{GlueInc} = $glueinc; $fchunk->{GluePref} = $gluepref; } push @$dest, @$src; } sub _appendchunks { my($chunks, $style, $mode, $class, $char, $preshift, $postshift) = @_; $preshift ||= 0; $postshift ||= 0; my($splittable, $glue, $gluedec, $glueinc, $gluepref) = (0) x 5; if( exists $style->{font} && exists $style->{font}{subset_unicodes} ) { my $unicode = $PDFJ::Default{Jcode} eq 'SJIS' ? PDFJ::Unicode::s2u($char) : $PDFJ::Default{Jcode} eq 'EUC' ? PDFJ::Unicode::e2u($char) : unpack("n", $char); # UNICODE or UTF8 (UTF8 already converted to UNICODE) $style->{font}{subset_unicodes}{$unicode} = 1 if $unicode; } if( @$chunks ) { my $lastchunk = $chunks->[$#$chunks]; my $lastmode = $lastchunk->{Mode}; my $lastclass = $lastchunk->{Class}; my $lastruby = $lastchunk->{Style}{ruby}; $splittable = $style->{suffix} ? 0 : $lastmode eq 'O' ? 0 : $PDFJ::Default{Splittable}->[$lastclass][$class]; $glue = ($lastmode =~ /^[ON]$/ || $mode =~ /^[ON]$/) ? PDFJ::GlueNon : $PDFJ::Default{Glue}->[$lastclass][$class]; if( ($mode eq 'a' && $lastmode eq 'a' && $class == $lastclass && ($class == 11 || (!@$glue && !$splittable))) || ($style->{ruby} && $style->{ruby} eq $lastruby && ($class == 11 || $class == 17) && $class == $lastclass) ) { $lastchunk->{Count}++; $lastchunk->{String} .= $char; return; } ($glue, $gluedec, $glueinc, $gluepref) = _calcglue($glue); } push @$chunks, PDFJ::TextChunk->new($style, $mode, $class, $splittable, $glue, $gluedec, $glueinc, $gluepref, 1, $char, $preshift, $postshift); } sub _calcglue { my($glue) = @_; my($gluedec, $glueinc, $gluepref) = (0, 0, 0); if( @$glue ) { my($gmin, $gnormal, $gmax, $gpref) = @$glue; ($glue, $gluedec, $glueinc) = ( $gnormal / 1000, ($gnormal - $gmin) / 1000, ($gmax - $gnormal) / 1000 ); $gluepref = $gpref || 0; } else { ($glue, $gluedec, $glueinc, $gluepref) = (0, 0, 0, 0); } ($glue, $gluedec, $glueinc, $gluepref); } sub _lastclass { my($chunks) = @_; @$chunks ? $chunks->[$#$chunks]{Class} : ''; } sub _lastclasseq { my($chunks, $eqclass) = @_; @$chunks && $chunks->[$#$chunks]{Class} == $eqclass; } sub _lastmode { my($chunks) = @_; @$chunks ? $chunks->[$#$chunks]{Mode} : ''; } sub splittext { my($self, $str) = @_; return [] if $str eq ''; if( UNIVERSAL::isa($self->style->{font}, "PDFJ::AFont") ) { &splittext_ASCII; } elsif( $PDFJ::Default{Jcode} eq 'SJIS' ) { &splittext_SJIS; } elsif( $PDFJ::Default{Jcode} eq 'EUC' ) { &splittext_EUC; } elsif( $PDFJ::Default{Jcode} eq 'UTF8' ) { &splittext_UTF8; } elsif( $PDFJ::Default{Jcode} eq 'UNICODE' ) { &splittext_UNICODE; } else { } } sub splittext_ASCII { my($self, $str) = @_; my $style = $self->style; my $result = []; my @c = split('', $str); for( my $j = 0; $j <= $#c; $j++ ) { my $c = $c[$j]; next if $c eq "\x00"; if( $c eq " " ) { _appendchunks($result, $style, 'a', 16, $c); } else { if( $style->{vh} ) { _appendchunks($result, $style, 'a', 11, $c); } elsif( $c =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } elsif( $c =~ /[,. ]/ && _lastclasseq($result, 14) && $c[$j+1] =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } else { _appendchunks($result, $style, 'a', 17, $c); } } } $result; } sub splittext_UTF8 { my($self, $str) = @_; require PDFJ::Unicode; $self->splittext_UNICODE(PDFJ::Unicode::utf8tounicode($str)); } sub splittext_UNICODE { my($self, $str) = @_; $str =~ s/^\xfe\xff//; my $style = $self->style; my $result = []; my @c = split('', $str); for( my $j = 0; $j <= $#c; $j += 2 ) { my $c = $c[$j].$c[$j+1]; my $ca = $c[$j+1]; if( $c ge "\xff\x61" && $c le "\xff\x9f" ) { _appendchunks($result, $style, 'h', 11, $c); } elsif( $c eq "\x00\x20" ) { #_appendchunks($result, $style, 'a', 16, $c); _appendchunks($result, $style, 'a', 16, $ca); } elsif( $c lt "\x00\x7f" ) { if( $style->{vh} ) { #_appendchunks($result, $style, 'a', 11, $c); _appendchunks($result, $style, 'a', 11, $ca); } elsif( $c ge "\x00\x30" && $c le "\x00\x39" ) { # /[0-9]/ #_appendchunks($result, $style, 'a', 14, $c); _appendchunks($result, $style, 'a', 14, $ca); } elsif( ($c eq "\x00\x2c" || $c eq "\x00\x2e") && # /[,.]/ _lastclasseq($result, 14) && $c[$j+2].$c[$j+3] ge "\x00\x30" && $c[$j+2].$c[$j+3] le "\x00\x39" ) { #_appendchunks($result, $style, 'a', 14, $c); _appendchunks($result, $style, 'a', 14, $ca); } else { #_appendchunks($result, $style, 'a', 17, $c); _appendchunks($result, $style, 'a', 17, $ca); } } else { my $class = $PDFJ::Default{Class}{UNICODE}{$c}; unless( defined $class ) { if( $c ge "\x30\x41" && $c le "\x30\x93" ) { # hirakana $class = 10; } else { $class = 11; } } my $preshift = ($PDFJ::Default{PreShift}{UNICODE}{$c} || 0) / 1000; my $postshift = ($PDFJ::Default{PostShift}{UNICODE}{$c} || 0) / 1000; _appendchunks($result, $style, 'z', $class, $c, $preshift, $postshift); } } $result; } sub splittext_EUC { my($self, $str) = @_; my $style = $self->style; my $result = []; my @c = split('', $str); for( my $j = 0; $j <= $#c; $j++ ) { my $c = $c[$j]; if( $c eq "\x8e" ) { _appendchunks($result, $style, 'h', 11, $c.$c[$j+1]); $j++; } elsif( $c eq "\x8f" ) { _appendchunks($result, $style, 'z', 11, $c.$c[$j+1].$c[$j+2]); $j += 2; } elsif( $c eq " " ) { _appendchunks($result, $style, 'a', 16, $c); } elsif( $c lt "\xa0" ) { if( $style->{vh} ) { _appendchunks($result, $style, 'a', 11, $c); } elsif( $c =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } elsif( $c =~ /[,. ]/ && _lastclasseq($result, 14) && $c[$j+1] =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } else { _appendchunks($result, $style, 'a', 17, $c); } } else { my $k = $c.$c[$j+1]; $j++; my $class = $PDFJ::Default{Class}{EUC}{$k}; unless( defined $class ) { if( $k ge "\xa4\xa1" && $k le "\xa4\xf3" ) { $class = 10; } else { $class = 11; } } my $preshift = ($PDFJ::Default{PreShift}{EUC}{$k} || 0) / 1000; my $postshift = ($PDFJ::Default{PostShift}{EUC}{$k} || 0) / 1000; _appendchunks($result, $style, 'z', $class, $k, $preshift, $postshift); } } $result; } sub splittext_SJIS { my($self, $str) = @_; my $style = $self->style; my $result = []; my @c = split('', $str); for( my $j = 0; $j <= $#c; $j++ ) { my $c = $c[$j]; if( $c ge "\x81" && $c le "\x9f" || $c ge "\xe0" && $c le "\xfc" ) { my $k = $c.$c[$j+1]; $j++; my $class = $PDFJ::Default{Class}{SJIS}{$k}; unless( defined $class ) { if( $k ge "\x82\x9f" && $k le "\x82\xf1" ) { $class = 10; } else { $class = 11; } } my $preshift = ($PDFJ::Default{PreShift}{SJIS}{$k} || 0) / 1000; my $postshift = ($PDFJ::Default{PostShift}{SJIS}{$k} || 0) / 1000; _appendchunks($result, $style, 'z', $class, $k, $preshift, $postshift); } elsif( $c eq " " ) { _appendchunks($result, $style, 'a', 16, $c); } elsif( $c ge "\xa1" && $c le "\xdf" ) { _appendchunks($result, $style, 'h', 11, $c); } else { if( $style->{vh} ) { _appendchunks($result, $style, 'a', 11, $c); } elsif( $c =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } elsif( $c =~ /[,. ]/ && _lastclasseq($result, 14) && defined $c[$j+1] && $c[$j+1] =~ /[0-9]/ ) { _appendchunks($result, $style, 'a', 14, $c); } else { _appendchunks($result, $style, 'a', 17, $c); } } } $result; } #-------------------------------------------------------------------------- package PDFJ::TextChunk; use strict; # chunk array index my %ChunkIndex = ( Style => 0, # PDFJ::TextStyle object Mode => 1, # description as above Class => 2, # description as above Splittable => 3, # 1 for splittable at pre-postion Glue => 4, # normal glue width GlueDec => 5, # decrease adjustable glue width GlueInc => 6, # increase adjustable glue width GluePref => 7, # glue preference Count => 8, # characters count String => 9, # characters string PreShift => 10, # postion shift at pre-postion PostShift => 11, # postion shift at post-postion GlueFix => 12, # fixed glue (to be calculated) Hyphened => 13, # 1 for splitted, 2 for hyphened RubyText => 14, # ruby PDFJ::Text object AltObj => 15, # alternative object for String RubyOverlap => 16, # ruby overlap size ); sub new { my($class, @args) = @_; my %hash = map { $_ => $args[ $ChunkIndex{ $_ } ] } keys(%ChunkIndex) ; bless \%hash, $class; } sub clone { my($self) = @_; my @clone = @$self; bless \@clone, ref($self); } #-------------------------------------------------------------------------- package PDFJ::ParagraphStyle; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Style); sub PStyle { PDFJ::ParagraphStyle->new(@_) } #-------------------------------------------------------------------------- package PDFJ::Paragraph; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Showable); sub Paragraph { PDFJ::Paragraph->new(@_) } sub new { my($class, $text, $style) = PDFJ::Util::methodargs([qw(text style)], @_); croak "paragraph text argument must be a PDFJ::Text object" unless UNIVERSAL::isa($text, 'PDFJ::Text'); $style = PDFJ::ParagraphStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); croak "paragraph style argument must be a PDFJ::ParagraphStyle object" unless UNIVERSAL::isa($style, 'PDFJ::ParagraphStyle'); croak "size specification missing" unless $style->{size}; croak "linefeed specification missing" unless exists $style->{linefeed}; croak "align specification missing" unless $style->{align}; my $self = bless { text => $text, style => $style }, $class; $self->{linefeed} = $style->{linefeed} =~ /(\d+)%/ ? $text->fontsize * $1 / 100 : $style->{linefeed}; my $lineskip = $self->{linefeed} - $text->fontsize; $lineskip = 0 if $lineskip < 0; $self->{preskip} = exists $style->{preskip} ? $style->{preskip} : $lineskip * $PDFJ::Default{ParaPreSkipRatio}; $self->{postskip} = exists $style->{postskip} ? $style->{postskip} : $lineskip * $PDFJ::Default{ParaPostSkipRatio}; my $labeltext = $style->{labeltext}; my $firstminindent = 0; if( $labeltext ) { if( UNIVERSAL::isa($labeltext, 'PDFJ::Showable') ) { $self->{labelobj} = $labeltext; } elsif( ref($labeltext) eq 'CODE' ) { $self->{labelobj} = &$labeltext(); } elsif( ref($labeltext) eq 'ARRAY' && ref($labeltext->[0]) eq 'CODE' ) { my($func, @args) = @$labeltext; $self->{labelobj} = &$func(@args); } elsif( ref($labeltext) ) { croak "unknown labeltext type"; } else { $self->{labelobj} = $labeltext; } $self->{labelobj} = PDFJ::Text->new($self->{labelobj}, $text->style) unless ref($self->{labelobj}); my $labelobjsize = $self->{labelobj}->size($self->text->direction); $firstminindent = $labelobjsize + $self->labelskip - $self->labelsize; $firstminindent = 0 if $firstminindent < 0; } $self->{beginindent} = exists $style->{beginindent} ? ref($style->{beginindent}) eq 'ARRAY' ? [@{$style->{beginindent}}] : [$style->{beginindent}] : [0]; if( $self->{beginindent}[0] < $firstminindent ) { $self->{beginindent}[1] = $self->{beginindent}[0] if @{$self->{beginindent}} == 1; $self->{beginindent}[0] = $firstminindent; } $self->{endindent} = exists $style->{endindent} ? ref($style->{endindent}) eq 'ARRAY' ? [@{$style->{endindent}}] : [$style->{endindent}] : [0]; my @lines = $text->_fold($self->linesizes, $style->{align}); $self->{lines} = \@lines; $self; } sub text { $_[0]->{text} } sub linesnum { scalar(@{$_[0]->{lines}}) } sub line { $_[0]->{lines}->[$_[1]] } sub labelsize { $_[0]->{style}->{labelsize} || 0 } sub labelskip { $_[0]->{style}->{labelskip} || 0 } sub beginpadding { $_[0]->{style}->{beginpadding} || 0 } sub beginindents { scalar @{$_[0]->{beginindent}} } sub beginindent { my($self, $idx) = @_; my $count = @{$self->{beginindent}}; if( $idx < $count ) { $self->{beginindent}[$idx]; } else { $self->{beginindent}[$count - 1]; } } sub endindents { scalar @{$_[0]->{endindent}} } sub endindent { my($self, $idx) = @_; my $count = @{$self->{endindent}}; if( $idx < $count ) { $self->{endindent}[$idx]; } else { $self->{endindent}[$count - 1]; } } sub _size { $_[0]->{style}{size} } sub linesizes { my($self) = @_; my @linesizes; my $count = $self->beginindents > $self->endindents ? $self->beginindents : $self->endindents; for( my $j = 0; $j < $count; $j++ ) { push @linesizes, $self->_size - $self->beginpadding - $self->labelsize - $self->beginindent($j) - $self->endindent($j); } \@linesizes; } sub linefeed { $_[0]->{linefeed} } sub preskip { $_[0]->{preskip} || 0 } sub postskip { $_[0]->{postskip} || 0 } sub nobreak { $_[0]->{style}->{nobreak} } sub postnobreak { $_[0]->{style}->{postnobreak} } sub float { $_[0]->{style}->{float} || "" } sub breakable { my($self, $blockdirection) = @_; return 0 if $self->nobreak; my $direction = $self->text->direction; if( $direction eq 'H' ) { $blockdirection eq 'V' ? 1 : 0; } else { $blockdirection eq 'V' ? 0 : 1; } } sub _linessize { my($self) = @_; $self->linesnum ? $self->text->fontsize + ($self->linesnum - 1) * $self->linefeed : 0; } sub break { my($self, $sizes) = PDFJ::Util::listargs2ref(1, 0, PDFJ::Util::methodargs([qw(sizes)], @_)); my @sizes = @$sizes; my $unbreakable = $self->nobreak; my $lastsize = $sizes[$#sizes]; my @result; my @lines = @{$self->{lines}}; my @beginindents = @{$self->{beginindent}}; my @endindents = @{$self->{endindent}}; my $second; while( @lines ) { my $size = @sizes ? shift(@sizes) : $lastsize; my $count = $unbreakable ? ($size < $self->_linessize ? 0 : scalar(@lines)) : ($size < $self->text->fontsize ? 0 : int(($size - $self->text->fontsize) / $self->linefeed) + 1); return if !$count && !@sizes; my @blines = splice @lines, 0, $count; my @bbi = splice @beginindents, 0, $count; @beginindents = ($bbi[$#bbi]) unless @beginindents; my @bei = splice @endindents, 0, $count; @endindents = ($bei[$#bei]) unless @endindents; my $bpara = bless {text => $self->{text}, style => $self->{style}, linefeed => $self->{linefeed}, preskip => $self->{preskip}, postskip => $self->{postskip}, beginindent => \@bbi, endindent => \@bei, labelobj => ($second ? undef : $self->{labelobj}), lines => \@blines}, ref($self); $second = 1 if @blines; push @result, $bpara; } @result; } sub width { my($self) = @_; $self->text->direction eq 'H' ? $self->_size : $self->_linessize; } sub height { my($self) = @_; $self->text->direction eq 'H' ? $self->_linessize : $self->_size; } sub left { my($self) = @_; $self->text->direction eq 'H' ? 0 : - ($self->_linessize - $self->text->fontsize / 2); } sub right { my($self) = @_; $self->text->direction eq 'H' ? $self->_size : $self->text->fontsize / 2; } sub top { my($self) = @_; $self->text->direction eq 'H' ? $self->text->fontsize * $PDFJ::Default{HBaseHeight} : 0; } sub bottom { my($self) = @_; $self->text->direction eq 'H' ? - ($self->_linessize - $self->text->fontsize * $PDFJ::Default{HBaseHeight}) : $self->_size; } sub size { my($self, $direction) = @_; if( $direction eq 'H' ) { $self->width; } elsif( $direction eq 'V' ) { $self->height; } else { $self->_size; } } sub _show { my($self, $page, $x, $y) = @_; for( my $j = 0; $j < $self->linesnum; $j++ ) { ($x, $y) = $self->_showline($page, $x, $y, $j); } ($x, $y); } sub _showline { my($self, $page, $x, $y, $line) = @_; return unless $line < $self->linesnum; my $style = $self->{style}; my $start = $self->line($line)->{Start}; my $count = $self->line($line)->{Count}; my $fixedglues = $self->line($line)->{FixedGlues}; my $shift = $self->line($line)->{Shift} + $self->beginpadding + $self->labelsize + $self->beginindent($line); my $linefeed = $self->linefeed; my $text = $self->text; my $tstyle = $text->style; croak "no font specification" unless exists $tstyle->{font}; my $direction = $tstyle->{font}{direction}; if( $line == 0 && $self->{labelobj} ) { my($lx, $ly) = $direction eq 'H' ? ($x + $self->beginpadding, $y) : ($x, $y - $self->beginpadding); $self->{labelobj}->show($page, $lx, $ly); } my($nextx, $nexty); if( $direction eq 'H' ) { ($nextx, $nexty) = ($x, $y - $linefeed); $x += $shift; } else { ($nextx, $nexty) = ($x - $linefeed, $y); $y -= $shift; } $text->_showpart($page, $x, $y, $start, $count, $fixedglues); ($nextx, $nexty); } #-------------------------------------------------------------------------- package PDFJ::NewBlock; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::BlockElement); sub NewBlock { PDFJ::NewBlock->new(@_) } sub new { my($class) = @_; bless \$class, $class; } #-------------------------------------------------------------------------- package PDFJ::BlockSkip; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::BlockElement); sub BlockSkip { PDFJ::BlockSkip->new(@_); } sub new { my($class, $skip) = PDFJ::Util::methodargs([qw(skip)], @_); bless {skip => $skip}, $class; } sub size { $_[0]->{skip} || 0 } #-------------------------------------------------------------------------- package PDFJ::BlockStyle; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Style); sub BStyle { PDFJ::BlockStyle->new(@_) } #-------------------------------------------------------------------------- package PDFJ::Block; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Showable); sub Block { PDFJ::Block->new(@_); } sub new { my($class, $direction, $objects, $style) = PDFJ::Util::listargs2ref(2, 1, PDFJ::Util::methodargs([qw(direction objects style)], @_)); croak "block direction argument must be H or V or R" unless $direction =~ /^H|V|R$/; $style = PDFJ::BlockStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); croak "block style argument must be a PDFJ::BlockStyle object" unless UNIVERSAL::isa($style, 'PDFJ::BlockStyle'); my @objects = @$objects; for( my $j = 0; $j < @objects; $j++ ) { if( UNIVERSAL::isa($objects[$j], 'PDFJ::BlockElement') ) { # OK } elsif( $objects[$j] =~ /^\d+$/ ) { $objects[$j] = PDFJ::BlockSkip->new($objects[$j]); } else { croak "illegal Block element: $objects[$j]" } } my $self = bless { direction => $direction, objects => \@objects, xpreshift => 0, xpostshift => 0, ypreshift => 0, ypostshift => 0, style => $style }, $class; $self->_calcsize; $self->adjustwidth($style->{width}) if $style->{width}; $self->adjustheight($style->{height}) if $style->{height}; $self; } sub break { my($self, $sizes) = PDFJ::Util::listargs2ref(1, 0, PDFJ::Util::methodargs([qw(sizes)], @_)); my @sizes = @$sizes; my $unbreakable = $self->nobreak; my $nofirstfloat = $self->nofirstfloat; my $repeatheader = $self->repeatheader; my $lastsize = $sizes[$#sizes]; my $direction = $self->{direction} eq 'V' ? 'V' : 'H'; my @result; my @objects = @{$self->{objects}}; my @repeatheader = $repeatheader ? @objects[0..($repeatheader - 1)] : (); my @reserve; while( @objects || @reserve ) { my $size = @sizes ? shift(@sizes) : $lastsize; unshift @objects, splice(@reserve); my @bobjects; if( $unbreakable ) { @bobjects = splice @objects if $size >= $self->size($direction); } else { my $bsize = $self->padding * 2; while( $bsize < $size && @objects ) { my $obj = $objects[0]; my $float = $obj->float; if( $float && @reserve ) { push @reserve, shift(@objects); next; } if( $float eq 'b' && $nofirstfloat && !@result ) { push @reserve, shift(@objects); next; } my $inspos = _inspos(\@bobjects, $float); my $skipsize = 0; if( $inspos == 0 ) { $skipsize = $obj->postskip + $bobjects[$inspos]->preskip if @bobjects; } elsif( $inspos == @bobjects ) { $skipsize = $bobjects[$inspos - 1]->postskip + $obj->preskip; } else { $skipsize = $obj->preskip + $obj->postskip; } my $osize = $obj->size($direction); if( UNIVERSAL::isa($obj, 'PDFJ::NewBlock') ) { shift(@objects); last if @bobjects; } elsif( $bsize + $skipsize + $osize <= $size ) { splice @bobjects, $inspos, 0, shift(@objects); $bsize += $skipsize + $osize; } elsif( $obj->breakable($self->{direction}) ) { my @bsizes = ($size - $bsize - $skipsize, map {$_ - $self->padding * 2} (@sizes ? (@sizes) : ($lastsize))); my @parts = $obj->break(@bsizes); if( @parts ) { $obj = $parts[0]; my $osize = $obj->size($direction); if( $osize ) { $bsize += $skipsize + $osize; shift @objects; unshift @objects, @parts; splice @bobjects, $inspos, 0, shift(@objects); } else { shift @parts; shift @objects; unshift @objects, @parts; } last; } else { carp "$obj->break(@bsizes) fails"; return; } } else { if( $float ) { push @reserve, shift(@objects); } else { last; } } } } while( @bobjects && $bobjects[$#bobjects]->postnobreak && @objects ) { unshift @objects, pop(@bobjects); } if( !@bobjects && !@sizes ) { carp "break fails"; return; } if( $repeatheader && (@bobjects >= $repeatheader) && @objects ) { unshift @objects, @repeatheader; } my $bobj; %$bobj = %$self; $bobj->{objects} = \@bobjects; delete $bobj->{indents}; bless $bobj, ref($self); $bobj->_calcsize; push @result, $bobj; } @result; } sub _inspos { # NOT method my($objects, $float) = @_; my $inspos; if( $float eq 'b' ) { $inspos = 0; while( $inspos < @$objects && $objects->[$inspos]->float eq 'b' ) { $inspos++; } } elsif( $float eq 'e' ) { $inspos = @$objects; } else { $inspos = @$objects; while( $inspos > 0 && $objects->[$inspos - 1]->float eq 'e' ) { $inspos--; } } $inspos; } sub _calcsize { my($self) = @_; my($width, $height) = (0, 0); my $objnum = @{$self->{objects}}; my $adjust = $self->{style}{adjust}; my $align = $self->align; if( $self->{direction} eq 'V' ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( $j > 0 ) { $height += $self->{objects}->[$j-1]->postskip + $obj->preskip; } if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $width = $width < $obj->width ? $obj->width : $width; $height += $obj->height; } elsif( UNIVERSAL::isa($obj, 'PDFJ::BlockElement') ) { $height += $obj->size($self->{direction}); } else { croak "illegal block element"; } } if( $adjust ) { for my $obj(@{$self->{objects}}) { $obj->adjustwidth($width) if UNIVERSAL::isa($obj, 'PDFJ::Block'); } } my @indents; if( $align =~ /c/ ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $indents[$j] = ($width - $obj->width) / 2; } } } elsif( $align =~ /r/ ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $indents[$j] = $width - $obj->width; } } } $self->{indents} = \@indents; } else { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( $j > 0 ) { $width += $self->{objects}->[$j-1]->postskip + $obj->preskip; } if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $height = $height < $obj->height ? $obj->height : $height; $width += $obj->width; } elsif( UNIVERSAL::isa($obj, 'PDFJ::BlockElement') ) { $width += $obj->size($self->{direction}); } else { croak "illegal block element"; } } if( $adjust ) { for my $obj(@{$self->{objects}}) { $obj->adjustheight($height) if UNIVERSAL::isa($obj, 'PDFJ::Block'); } } my @indents; if( $align =~ /m/ ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $indents[$j] = ($height - $obj->height) / 2; } } } elsif( $align =~ /b/ ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $indents[$j] = $height - $obj->height; } } } $self->{indents} = \@indents; } $self->{width} = $width; $self->{height} = $height; } sub padding { $_[0]->{style}{padding} || 0 } sub width { my($self) = @_; $self->{width} + $self->padding * 2 + $self->{xpreshift} + $self->{xpostshift} + ($self->direction eq 'H' ? 0 : $self->beginpadding); } sub height { my($self) = @_; $self->{height} + $self->padding * 2 + $self->{ypreshift} + $self->{ypostshift} + ($self->direction eq 'H' ? $self->beginpadding : 0); } sub size { my($self, $direction) = @_; if( $direction eq 'H' ) { $self->width; } else { $self->height; } } sub left { 0 } sub right { $_[0]->width } sub top { 0 } sub bottom { - $_[0]->height } sub preskip { $_[0]->{style}{preskip} || 0 } sub postskip { $_[0]->{style}{postskip} || 0 } sub align { $_[0]->{style}{align} || "" } sub nobreak { $_[0]->{style}{nobreak} } sub postnobreak { $_[0]->{style}{postnobreak} } sub repeatheader { $_[0]->{style}{repeatheader} || 0 } sub float { $_[0]->{style}->{float} || "" } sub nofirstfloat { $_[0]->{style}{nofirstfloat} } sub beginpadding { $_[0]->{style}{beginpadding} || 0 } sub direction { $_[0]->{direction} } sub breakable { my($self, $blockdirection) = @_; $self->nobreak ? 0 : $blockdirection eq $self->{direction} ? 1 : 0; } sub adjustwidth { my($self, $size) = PDFJ::Util::methodargs([qw(size)], @_); return unless $size; my $align = $self->align; return $self if $self->width >= $size; $size -= $self->width; if( $align =~ /r/ ) { $self->{xpreshift} = $size; } elsif( $align =~ /c/ ) { $self->{xpreshift} = $size / 2; $self->{xpostshift} = $size / 2; } else { # l $self->{xpostshift} = $size; } $self; } sub adjustheight { my($self, $size) = PDFJ::Util::methodargs([qw(size)], @_); return unless $size; my $align = $self->align; return $self if $self->height >= $size; $size -= $self->height; if( $align =~ /b/ ) { $self->{ypreshift} = $size; } elsif( $align =~ /m/ ) { $self->{ypreshift} = $size / 2; $self->{ypostshift} = $size / 2; } else { # t $self->{ypostshift} = $size; } $self; } sub _show { my($self, $page, $x, $y) = @_; if( $self->direction eq 'H' ) { $y -= $self->beginpadding; } else { $x += $self->beginpadding; } my $style = $self->{style}; if( $style->{withbox} ) { my $withbox = $style->{withbox}; my $withboxstyle = $style->{withboxstyle}; my $shape = PDFJ::Shape->new; $shape->box(0, 0, $self->width, - $self->height, $withbox, $withboxstyle); $shape->show($page, $x, $y); } $x += $self->padding + $self->{xpreshift}; $y -= $self->padding + $self->{ypreshift}; my $objnum = @{$self->{objects}}; if( $self->{direction} eq 'V' ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; my $indent = $self->{indents}->[$j] || 0; if( $j > 0 ) { $y -= $self->{objects}->[$j-1]->postskip + $obj->preskip; } if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $obj->show($page, $x + $indent, $y, 'tl'); $y -= $obj->height; } elsif( UNIVERSAL::isa($obj, 'PDFJ::BlockElement') ) { $y -= $obj->size($self->{direction}); } elsif( $obj =~ /^\d+$/ ) { $y -= $obj; } else { croak "illegal block element"; } } } elsif( $self->{direction} eq 'H' ) { for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; my $indent = $self->{indents}->[$j] || 0; if( $j > 0 ) { $x += $self->{objects}->[$j-1]->postskip + $obj->preskip; } if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $obj->show($page, $x, $y - $indent, 'tl'); $x += $obj->width; } elsif( UNIVERSAL::isa($obj, 'PDFJ::BlockElement') ) { $x += $obj->size($self->{direction}); } else { croak "illegal block element"; } } } elsif( $self->{direction} eq 'R' ) { $x += $self->{width}; for( my $j = 0; $j < $objnum; $j++ ) { my $obj = $self->{objects}->[$j]; my $indent = $self->{indents}->[$j] || 0; if( $j > 0 ) { $x -= $self->{objects}->[$j-1]->postskip + $obj->preskip; } if( UNIVERSAL::isa($obj, 'PDFJ::Showable') ) { $obj->show($page, $x, $y - $indent, 'tr'); $x -= $obj->width; } elsif( UNIVERSAL::isa($obj, 'PDFJ::BlockElement') ) { $x -= $obj->size($self->{direction}); } else { croak "illegal block element"; } } } } #-------------------------------------------------------------------------- package PDFJ::Image; use PDFJ::Object; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Showable); sub new { my($class, $docobj, $src, $pxwidth, $pxheight, $width, $height, $padding, $colorspace) = @_; my($ext) = $src =~ /([^\.]+)$/; croak "unknown image file extention: $ext" unless $ext =~ /^jpe?g$/i; if( $src =~ /^http:/i ) { new_url($class, $docobj, $src, $pxwidth, $pxheight, $width, $height, $padding, $colorspace); } else { new_file($class, $docobj, $src, $pxwidth, $pxheight, $width, $height, $padding, $colorspace); } } sub new_url { my($class, $docobj, $url, $pxwidth, $pxheight, $width, $height, $padding, $colorspace) = @_; $width ||= $pxwidth; $height ||= $pxheight; $colorspace ||= 'DeviceRGB'; $colorspace = $colorspace =~ /^rgb$/i ? 'DeviceRGB' : $colorspace =~ /^gray$/i ? 'DeviceGray' : $colorspace =~ /^cmyk$/i ? 'DeviceCMYK' : 'DeviceRGB'; my $num = $docobj->_nextimagenum; my $name = "I$num"; my $image = $docobj->indirect(stream(dictionary => { Name => name($name), Type => name("XObject"), Subtype => name("Image"), Width => number($pxwidth), Height => number($pxheight), BitsPerComponent => 8, ColorSpace => name($colorspace), FFilter => name("DCTDecode"), F => { FS => name("URL"), F => string($url), }, Length => 0, # No Data }, stream => '')); $docobj->_registimage($name, $image); bless { name => $name, image => $image, width => $width, height => $height, padding => $padding }, $class; } sub new_file { my($class, $docobj, $file, $pxwidth, $pxheight, $width, $height, $padding, $colorspace) = @_; $width ||= $pxwidth; $height ||= $pxheight; $colorspace ||= 'DeviceRGB'; $colorspace = $colorspace =~ /^rgb$/i ? 'DeviceRGB' : $colorspace =~ /^gray$/i ? 'DeviceGray' : $colorspace =~ /^cmyk$/i ? 'DeviceCMYK' : 'DeviceRGB'; my $num = $docobj->_nextimagenum; my $name = "I$num"; my($encoded, $filter) = $docobj->_makestream($file, "DCTDecode"); my $image = $docobj->indirect(stream(dictionary => { Name => name($name), Type => name("XObject"), Subtype => name("Image"), Width => number($pxwidth), Height => number($pxheight), BitsPerComponent => 8, ColorSpace => name($colorspace), Filter => $filter, Length => length($encoded), }, stream => $encoded)); $docobj->_registimage($name, $image); bless { name => $name, image => $image, width => $width, height => $height, padding => $padding }, $class; } sub image { $_[0]->{image} } sub width { $_[0]->{width} + $_[0]->padding * 2 } sub height { $_[0]->{height} + $_[0]->padding * 2 } sub padding { $_[0]->{padding} || 0 } sub left { 0 } sub right { $_[0]->width } sub top { $_[0]->height } sub bottom { 0 } sub size { my($self, $direction) = @_; $direction eq 'H' ? $self->width : $self->height; } sub setsize { my($self, $width, $height) = @_; $self->{width} = $width; $self->{height} = $height; $self; } sub setpadding { my($self, $padding) = @_; $self->{padding} = $padding; } sub _show { my($self, $page, $x, $y) = @_; $x += $self->padding; $y += $self->padding; my $width = $self->{width}; my $height = $self->{height}; my $name = $self->{name}; $page->addcontents("q", numbers($width, 0, 0, $height, $x, $y), "cm /$name Do Q"); $page->useimage($self); } #-------------------------------------------------------------------------- package PDFJ::Color; use PDFJ::Object; use Carp; use strict; sub Color { PDFJ::Color->new(@_) } sub new { my $class = shift; if( @_ == 1 ) { new1($class, @_); } elsif( @_ == 3 ) { new3($class, @_); } else { croak "Color arguments must be one or three"; } } sub new1 { my($class, $value) = PDFJ::Util::methodargs([qw(value)], @_); #my $class = shift; my $self; if( ref($value) eq 'ARRAY' ) { $self = bless { type => 'rgb', value => $value }, $class; } elsif( $value =~ /^#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$/ ) { my(@rgb) = map {oct("0x$_")/256} ($1,$2,$3); $self = bless { type => 'rgb', value => \@rgb }, $class; } else { $self = bless { type => 'gray', value => $value }, $class; } $self; } sub new3 { my($class, @rgb) = @_; bless { type => 'rgb', value => \@rgb }, $class; } sub fill { my($self) = @_; if( $self->{type} eq 'gray' ) { (number($self->{value}), "g"); } else { # 'rgb' (numbers(@{$self->{value}}), "rg"); } } sub stroke { my($self) = @_; if( $self->{type} eq 'gray' ) { (number($self->{value}), "G"); } else { # 'rgb' (numbers(@{$self->{value}}), "RG"); } } #-------------------------------------------------------------------------- package PDFJ::ShapeStyle; use Carp; use strict; use PDFJ::Object; use vars qw(@ISA); @ISA = qw(PDFJ::Style); sub SStyle { PDFJ::ShapeStyle->new(@_) } sub pdf { my($self) = @_; my @result; push @result, $self->{fillcolor}->fill if $self->{fillcolor}; push @result, $self->{strokecolor}->stroke if $self->{strokecolor}; push @result, number($self->{linewidth}), "w" if $self->{linewidth}; if( $self->{linedash} ) { my($dash, $gap, $phase) = ref($self->{linedash}) eq 'ARRAY' ? @{$self->{linedash}} : split(/\s*,\s*/, $self->{linedash}); $phase ||= 0; push @result, array([numbers($dash, $gap)]), number($phase), "d"; } @result; } #-------------------------------------------------------------------------- package PDFJ::Shape; use PDFJ::Object; use Carp; use strict; use vars qw(@ISA); @ISA = qw(PDFJ::Showable); sub Shape { PDFJ::Shape->new(@_) } sub new { my($class, $style) = PDFJ::Util::methodargs([qw(style)], @_); my $self = bless { left => 0, top => 0, right => 0, bottom => 0, pdf => [] }, $class; $self->style($style) if $style; $self; } sub padding { $_[0]->{style}{padding} || 0 } sub left { $_[0]->{left} - $_[0]->padding } sub right { $_[0]->{right} + $_[0]->padding } sub top { $_[0]->{top} + $_[0]->padding } sub bottom { $_[0]->{bottom} - $_[0]->padding } sub width { $_[0]->{right} - $_[0]->{left} + $_[0]->padding * 2 } sub height { $_[0]->{top} - $_[0]->{bottom} + $_[0]->padding * 2 } sub preskip { $_[0]->{style}{preskip} || 0 } sub postskip { $_[0]->{style}{postskip} || 0 } sub postnobreak { $_[0]->{style}{postnobreak} } sub float { $_[0]->{style}->{float} || "" } sub pdf { @{$_[0]->{pdf}} } sub size { my($self, $direction) = @_; $direction eq 'H' ? $self->width : $self->height; } sub setboundary { my($self, $x, $y) = @_; if( $x < $self->{left} ) { $self->{left} = $x; } elsif( $x > $self->{right} ) { $self->{right} = $x; } if( $y < $self->{bottom} ) { $self->{bottom} = $y; } elsif( $y > $self->{top} ) { $self->{top} = $y; } } sub appendpdf { my($self, @pdf) = @_; push @{$self->{pdf}}, @pdf; $self; } sub appendobj { my($self, $obj, @args) = @_; push @{$self->{objects}}, [$obj, @args]; $self; } sub add_link { my($self, $rect, $name) = @_; $self->{link}{join(',', @$rect)} = $name; } sub show_link { my($self, $page, $x, $y) = @_; $x ||= 0; $y ||= 0; for my $rect(keys %{$self->{link}}) { my $name = $self->{link}{$rect}; my @rect = split(',', $rect); $rect[0] += $x; $rect[1] += $y; $rect[2] += $x; $rect[3] += $y; $page->add_link(\@rect, $name); } } sub _show { my($self, $page, $x, $y) = @_; $x += $self->padding; $y += $self->padding; my @stylepdf; if( $self->{style} ) { @stylepdf = $self->{style}->pdf if UNIVERSAL::can($self->{style}, 'pdf'); } $page->addcontents("q", numbers(1, 0, 0, 1, $x, $y), "cm", @stylepdf, $self->pdf); if( $self->{objects} ) { for my $objspec(@{$self->{objects}}) { my($obj, @args) = @$objspec; $obj->show($page, @args); } } $page->addcontents("Q"); $self->show_link($page, $x, $y); } sub style { my($self, $style) = @_; $style = PDFJ::ShapeStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); croak "shape style argument must be a PDFJ::ShapeStyle object" unless UNIVERSAL::isa($style, 'PDFJ::ShapeStyle'); $self->{style} = $style; $self; } # General Graphic State operators sub gstatepush { my($self) = @_; $self->appendpdf("q"); } sub gstatepop { my($self) = @_; $self->appendpdf("Q"); } sub linewidth { my($self, $w) = @_; $self->appendpdf(number($w), "w"); } sub linedash { my($self, $dash, $gap, $phase) = @_; $phase ||= 0; $self->appendpdf(array([numbers($dash, $gap)]), number($phase), "d"); } sub ctm { my($self, @array) = @_; croak "ctm array must have 6 elements" unless @array == 6; $self->appendpdf(numbers(@array), "cm"); } # Color operators sub fillcolor { my($self, $color) = @_; croak "color argument must be a PDFJ::Color object" unless UNIVERSAL::isa($color, 'PDFJ::Color'); $self->appendpdf($color->fill); } sub strokecolor { my($self, $color) = @_; croak "color argument must be a PDFJ::Color object" unless UNIVERSAL::isa($color, 'PDFJ::Color'); $self->appendpdf($color->stroke); } sub fillgray { my($self, $g) = @_; $self->appendpdf(number($g), "g"); } sub strokegray { my($self, $g) = @_; $self->appendpdf(number($g), "G"); } sub fillrgb { my($self, $r, $g, $b) = @_; $self->appendpdf(numbers($r, $g, $b), "rg"); } sub strokergb { my($self, $r, $g, $b) = @_; $self->appendpdf(numbers($r, $g, $b), "RG"); } # Path segment operators # moves the current point to (x, y), omitting any connecting line segment sub moveto { my($self, $x, $y) = @_; $self->setboundary($x, $y); $self->appendpdf(numbers($x, $y), "m"); } # appends a straight line segment from the current point to (x, y). # The current point becomes (x, y). sub lineto { my($self, $x, $y) = @_; $self->setboundary($x, $y); $self->appendpdf(numbers($x, $y), "l"); } # appends a Bezier curve to the path. The curve extends # from the current point to (x3 ,y3) using (x1 ,y1) and (x2 ,y2) # as the Bezier control points. # The current point becomes (x3 ,y3). sub curveto { my($self, $x1, $y1, $x2, $y2, $x3, $y3) = @_; $self->setboundary($x1, $y1); $self->setboundary($x2, $y2); $self->setboundary($x3, $y3); $self->appendpdf(numbers($x1, $y1, $x2, $y2, $x3, $y3), "c"); } # omit 'v' and 'y' # adds a rectangle to the current path sub rectangle { my($self, $x, $y, $w, $h) = @_; $self->setboundary($x, $y); $self->setboundary($x + $w, $y + $h); $self->appendpdf(numbers($x, $y, $w, $h), "re"); } # closes the current subpath by appending a straight line segment # from the current point to the starting point of the subpath. sub closepath { my $self = shift; $self->appendpdf("h"); } # ends the path without filling or stroking it sub newpath { my $self = shift; $self->appendpdf("n"); } # strokes the path sub stroke { my $self = shift; $self->appendpdf("S"); } # closes and strokes the path sub closestroke { my $self = shift; $self->appendpdf("s"); } # fills the path using the non-zero winding number rule sub fill { my $self = shift; $self->appendpdf("f"); } # fills the path using the even-odd rule sub fill2 { my $self = shift; $self->appendpdf("f*"); } # Path macro sub line { my($self, $x, $y, $w, $h, $style) = PDFJ::Util::methodargs([qw(x y w h style)], @_); $style = PDFJ::ShapeStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); my @stylepdf; @stylepdf = $style->pdf if $style; my($x1, $y1, $x2, $y2) = ($x, $y, $x + $w, $y + $h); $self->setboundary($x1, $y1); $self->setboundary($x2, $y2); $self->appendpdf("q", @stylepdf) if @stylepdf; $self->appendpdf(numbers($x1, $y1), "m", numbers($x2, $y2), "l S"); $self->appendpdf("Q") if @stylepdf; $self; } sub textuline { my($self, $x, $y, $size, $fontsize, $style) = @_; my $yshift = $PDFJ::Default{ULine} * $fontsize / 1000; $self->line($x, $y + $yshift, $size, 0, $style); } sub textoline { my($self, $x, $y, $size, $fontsize, $style) = @_; my $yshift = $PDFJ::Default{OLine} * $fontsize / 1000; $self->line($x, $y + $yshift, $size, 0, $style); } sub textlline { my($self, $x, $y, $size, $fontsize, $style) = @_; my $xshift = $PDFJ::Default{LLine} * $fontsize / 1000; $self->line($x + $xshift, $y, 0, $size, $style); } sub textrline { my($self, $x, $y, $size, $fontsize, $style) = @_; my $xshift = $PDFJ::Default{RLine} * $fontsize / 1000; $self->line($x + $xshift, $y, 0, $size, $style); } sub box { my($self, $x, $y, $w, $h, $spec, $style) = PDFJ::Util::methodargs([qw(x y w h spec style)], @_); $spec = "s" unless $spec; $style = PDFJ::ShapeStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); my @stylepdf; @stylepdf = $style->pdf if $style; my($r); if( $spec =~ s/r(\d+)// ) { $r = $1; croak "too big radius for round box" if $r * 2 > abs($w) || $r * 2 > abs($h); } if( $w < 0 ) { $x += $w; $w = -$w; } if( $h < 0 ) { $y += $h; $h = -$h; } $self->setboundary($x, $y); $self->setboundary($x + $w, $y + $h); if( $spec ne 'n' ) { $self->appendpdf("q", @stylepdf) if @stylepdf; if( $r ) { my $bz = $r * 0.55228475; my @work = ( numbers($x+$w, $y+$h-$r), 'm', numbers($x+$w, $y+$h-$r+$bz), numbers($x+$w-$r+$bz, $y+$h), numbers($x+$w-$r, $y+$h), 'c', numbers($x+$r, $y+$h), 'l', numbers($x+$r-$bz, $y+$h), numbers($x, $y+$h-$r+$bz), numbers($x, $y+$h-$r), 'c', numbers($x, $y+$r), 'l', numbers($x, $y+$r-$bz), numbers($x+$r-$bz, $y), numbers($x+$r, $y), 'c', numbers($x+$w-$r, $y), 'l', numbers($x+$w-$r+$bz, $y), numbers($x+$w, $y+$r-$bz), numbers($x+$w, $y+$r), 'c', numbers($x+$w, $y+$h-$r), 'l' ); $self->appendpdf(@work); } else { $self->appendpdf(numbers($x, $y), "m", numbers($x, $y, $w, $h), "re"); } if( $spec eq 'sf' ) { $self->appendpdf("B"); } elsif( $spec eq 's' ) { $self->appendpdf("S"); } elsif( $spec eq 'f' ) { $self->appendpdf("f"); } elsif( $spec =~ /^([lrtb]+)(f?)$/ ) { croak "'lrtb' is inconsistent with 'rX'" if $r; my($side, $fill) = ($1, $2); if( $fill eq 'f' ) { $self->appendpdf("f"); } else { $self->appendpdf("n"); } $self->line($x, $y, 0, $h) if $side =~ /l/; $self->line($x + $w, $y, 0, $h) if $side =~ /r/; $self->line($x, $y + $h, $w, 0) if $side =~ /t/; $self->line($x, $y, $w, 0) if $side =~ /b/; } elsif( $spec eq 'n' ) { $self->appendpdf("n"); } else { croak "illegal strokefill argument: $spec"; } $self->appendpdf("Q") if @stylepdf; } if( $style && $style->{link} ) { $self->add_link([$x, $y, $x + $w, $y + $h], $style->{link}); } $self; } sub textbox { my($self, $direction, $x, $y, $size, $fontsize, $spec, $style) = @_; my(@bbox) = @{$PDFJ::Default{"SBox$direction"}}; grep {$_ = $_ * $fontsize / 1000} @bbox; if( $direction eq 'H' ) { $self->box( $x + $bbox[0], $y + $bbox[1], $size + $bbox[2] - $bbox[0] - $fontsize, $bbox[3] - $bbox[1], $spec, $style ); } else { $self->box( $x + $bbox[0], $y, $bbox[2] - $bbox[0] , $size + $bbox[3] - $bbox[1] - $fontsize, $spec, $style ); } } sub circle { my($self, $x, $y, $r, $spec, $arcarea, $style) = PDFJ::Util::methodargs([qw(x y r spec arcarea style)], @_); $self->ellipse($x, $y, $r, $r, $spec, $arcarea, $style); } sub ellipse { my($self, $x, $y, $xr, $yr, $spec, $arcarea, $style) = PDFJ::Util::methodargs([qw(x y xr yr spec arcarea style)], @_); $spec = "s" unless $spec; $style = PDFJ::ShapeStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); my @stylepdf; @stylepdf = $style->pdf if $style; $self->appendpdf("q", @stylepdf) if @stylepdf; my $xbz = $xr * 0.55228475; my $ybz = $yr * 0.55228475; my @pt = ( $x+$xr, $y, $x+$xr, $y+$ybz, $x+$xbz, $y+$yr, $x, $y+$yr, $x-$xbz, $y+$yr, $x-$xr, $y+$ybz, $x-$xr, $y, $x-$xr, $y-$ybz, $x-$xbz, $y-$yr, $x, $y-$yr, $x+$xbz, $y-$yr, $x+$xr, $y-$ybz, $x+$xr, $y, ); if( $arcarea ) { $arcarea--; $arcarea %= 4; $self->setboundary(@pt[$arcarea * 6, $arcarea * 6 + 1]); $self->setboundary(@pt[$arcarea * 6 + 6, $arcarea * 6 + 7]); $self->appendpdf(numbers(splice(@pt, $arcarea * 6, 2)), "m"); $self->appendpdf(numbers(splice(@pt, $arcarea * 6, 6)), "c"); } else { $self->setboundary($x - $xr, $y - $yr); $self->setboundary($x + $xr, $y + $yr); $self->appendpdf(numbers(splice(@pt, 0, 2)), "m"); $self->appendpdf(numbers(splice(@pt, 0, 6)), "c"); $self->appendpdf(numbers(splice(@pt, 0, 6)), "c"); $self->appendpdf(numbers(splice(@pt, 0, 6)), "c"); $self->appendpdf(numbers(splice(@pt, 0, 6)), "c"); } if( $spec eq 'sf' ) { $self->appendpdf("B"); } elsif( $spec eq 's' ) { $self->appendpdf("S"); } elsif( $spec eq 'f' ) { $self->appendpdf("f"); } $self->appendpdf("Q") if @stylepdf; $self; } sub polygon { my($self, $coords, $spec, $style) = PDFJ::Util::methodargs([qw(coords spec style)], @_); croak "coords argument must be an array ref" unless ref($coords) eq 'ARRAY'; croak "coords argument must have even elements" if @$coords % 2; my @work; for( my $j = 0; $j < @$coords; $j += 2 ) { push @work, numbers($coords->[$j], $coords->[$j + 1]); push @work, ($j == 0) ? 'm' : 'l'; } $style = PDFJ::ShapeStyle->new($style) if $style && (!ref($style) || ref($style) eq 'HASH'); my @stylepdf; @stylepdf = $style->pdf if $style; $self->appendpdf("q", @stylepdf) if @stylepdf; $self->appendpdf(@work); if( $spec eq 'sf' ) { $self->appendpdf("B"); } elsif( $spec eq 's' ) { $self->appendpdf("S"); } elsif( $spec eq 'f' ) { $self->appendpdf("f"); } $self->appendpdf("Q") if @stylepdf; $self; } sub obj { my($self, $obj, $showargs) = PDFJ::Util::listargs2ref(2, 0, PDFJ::Util::methodargs([qw(obj showargs)], @_)); $self->appendobj($obj, @$showargs); $self; } #-------------------------------------------------------------------------- package PDFJ::Page; use Carp; use strict; use PDFJ::Object; sub new { my($class, $docobj, $pagewidth, $pageheight) = @_; my $pagetree = $docobj->{pagetree}; $pagewidth ||= $docobj->pagewidth; $pageheight ||= $docobj->pageheight; my $page = $docobj->indirect(dictionary({ Type => name('Page'), Parent => $pagetree, Resources => {ProcSet => [name('PDF'), name('Text')], Font => {}}, MediaBox => [0, 0, $pagewidth, $pageheight], Contents => $docobj->indirect( contents_stream(dictionary => {}, stream => [])), })); push @{$docobj->{pagelist}}, $page; my $pagenum = @{$docobj->{pagelist}}; $pagetree->get('Kids')->push($page); $pagetree->get('Count')->add(1); my $self = bless { page => $page, pagenum => $pagenum, parent => $pagetree, docobj => $docobj, layer => 0, }, $class; push @{$docobj->{pageobjlist}}, $self; $self; } sub show { my($self, $obj, @args) = @_; $obj->show($self, @args); } sub docobj { my($self) = @_; $self->{docobj}; } sub page { my($self) = @_; $self->{page}; } sub pagenum { my($self) = @_; $self->{pagenum}; } sub getlayer { my($self) = @_; $self->{layer}; } sub addcontents { my($self, @str) = @_; my @tmp = map {($_, " ")} @str; $self->page->get('Contents')->append(\@tmp, $self->getlayer); } sub layer { my($self, $layer) = PDFJ::Util::methodargs([qw(layer)], @_); $self->{layer} = $layer; $self; } sub usefonts { my($self, @names) = @_; my $docobj = $self->docobj; my $resources = $self->page->get('Resources'); for my $name(@names) { $resources->get('Font')->set($name, $docobj->_font($name)); } } sub useimage { my($self, $imageobj) = @_; my $resources = $self->page->get('Resources'); $resources->get('ProcSet')->add(name('ImageC')); $resources->set(XObject => {}) unless $resources->exists('XObject'); $resources->get('XObject')->set($imageobj->{name}, $imageobj->{image}); } sub dest { my($self, $argtype, @args) = @_; array([$self->page, name($argtype), map {$_ eq '' ? null : $_} @args]); } sub add_link { my($self, $rect, $name) = @_; $self->{link}{join(',', @$rect)} = $name; } sub add_annot { my($self, %spec) = @_; $spec{Type} = name('Annot'); my $docobj = $self->docobj; unless( $self->page->exists('Annots') ) { $self->page->set(Annots => []); } $self->page->get('Annots')->push($docobj->indirect(dictionary(\%spec))); } sub solve_link { my($self) = @_; my $docobj = $self->docobj; for my $rect(keys %{$self->{link}}) { my $name = $self->{link}{$rect}; my @rect = split(',', $rect); if( $name =~ /^URI:(.+)/ ) { my $uri = $1; $self->add_annot( Subtype => name('Link'), Rect => [@rect], Border => [0,0,0], A => { Type => name('Action'), S => name('URI'), URI => string(PDFJ::Util::uriencode($uri)) }, ); } else { my $dest = $docobj->dest($name) or croak "missing dest '$name'"; $self->add_annot( Subtype => name('Link'), Rect => [@rect], Border => [0,0,0], Dest => $dest, ); } } } #-------------------------------------------------------------------------- package PDFJ::File; use strict; use PDFJ::Object; sub new { my($class, $version, $handle, $objtable, $rootobj) = @_; binmode $handle; bless { version => $version, # PDF version handle => $handle, # file handle objtable => $objtable, # PDFJ::ObjTable object rootobj => $rootobj, # document catalog object objposlist => [], xrefpos => 0, tell => 0 }, $class; } sub print { my $self = shift; $self->print_header; $self->print_body; $self->print_xref; $self->print_trailer; } sub print_header { my $self = shift; my $handle = $self->{handle}; my $version = $self->{version}; #print $handle "%PDF-$version\n"; my $header = "%PDF-$version\n"; $self->{tell} += length($header); print $handle $header; } sub print_body { my $self = shift; my $handle = $self->{handle}; my $objtable = $self->{objtable}; return unless $objtable->lastobjnum; for my $objnum(1 .. $objtable->lastobjnum) { # $self->{objposlist}->[$objnum] = $handle->tell; # $objtable->get($objnum)->print($handle); $self->{objposlist}->[$objnum] = $self->{tell}; $self->{tell} += $objtable->get($objnum)->print($handle); } } sub print_xref { my $self = shift; my $handle = $self->{handle}; # $self->{xrefpos} = $handle->tell; $self->{xrefpos} = $self->{tell}; print $handle "xref\n"; my $objtable = $self->{objtable}; my $lastobjnum = $objtable->lastobjnum; my $entries = $lastobjnum + 1; print $handle "0 $entries\n"; print $handle "0000000000 65535 f \n"; if( $lastobjnum ) { for my $objnum(1 .. $lastobjnum) { printf $handle "%010.10d %05.5d n \n", $self->{objposlist}->[$objnum], $objtable->get($objnum)->{gennum}; } } print $handle "\n"; } sub print_trailer { my $self = shift; my $handle = $self->{handle}; my $xrefpos = $self->{xrefpos}; my $objtable = $self->{objtable}; my $traildic = dictionary({ Size => $objtable->lastobjnum + 1, Root => $self->{rootobj} }); print $handle "trailer\n", $traildic->output, "\nstartxref\n$xrefpos\n%%EOF\n"; } #-------------------------------------------------------------------------- package PDFJ::ObjTable; use strict; sub new { my($class) = @_; bless {objlist => [undef]}, $class; } sub lastobjnum { my $self = shift; $#{$self->{objlist}}; } sub get { my($self, $idx) = @_; $self->{objlist}->[$idx]; } sub set { my($self, $idx, $obj) = @_; $self->{objlist}->[$idx] = $obj; } 1; #-------------------------------------------------------------------------- # for SelfLoader in PDFJ::AFont package PDFJ::AFont; __DATA__ sub fontwidth_Courier { [ 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 ] } sub fontwidth_Courier_Bold { [ 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 ] } sub fontwidth_Courier_BoldOblique { [ 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 ] } sub fontwidth_Courier_Oblique { [ 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600 ] } sub fontwidth_Helvetica { [ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 221, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 556, 556, 167, 556, 556, 556, 556, 191, 333, 556, 333, 333, 500, 500, 278, 556, 556, 556, 278, 278, 537, 350, 222, 333, 333, 556, 1000, 1000, 278, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 1000, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 1000, 278, 370, 278, 278, 278, 278, 556, 778, 1000, 365, 278, 278, 278, 278, 278, 889, 278, 278, 278, 278, 278, 278, 222, 611, 944, 611, 278, 278, 278, 278 ] } sub fontwidth_Helvetica_Bold { [ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 556, 556, 167, 556, 556, 556, 556, 238, 500, 556, 333, 333, 611, 611, 278, 556, 556, 556, 278, 278, 556, 350, 278, 500, 500, 556, 1000, 1000, 278, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 1000, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 1000, 278, 370, 278, 278, 278, 278, 611, 778, 1000, 365, 278, 278, 278, 278, 278, 889, 278, 278, 278, 278, 278, 278, 278, 611, 944, 611, 278, 278, 278, 278 ] } sub fontwidth_Helvetica_BoldOblique { [ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 556, 556, 167, 556, 556, 556, 556, 238, 500, 556, 333, 333, 611, 611, 278, 556, 556, 556, 278, 278, 556, 350, 278, 500, 500, 556, 1000, 1000, 278, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 1000, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 1000, 278, 370, 278, 278, 278, 278, 611, 778, 1000, 365, 278, 278, 278, 278, 278, 889, 278, 278, 278, 278, 278, 278, 278, 611, 944, 611, 278, 278, 278, 278 ] } sub fontwidth_Helvetica_Oblique { [ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 556, 556, 167, 556, 556, 556, 556, 191, 333, 556, 333, 333, 500, 500, 278, 556, 556, 556, 278, 278, 537, 350, 222, 333, 333, 556, 1000, 1000, 278, 611, 278, 333, 333, 333, 333, 333, 333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 1000, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 1000, 278, 370, 278, 278, 278, 278, 556, 778, 1000, 365, 278, 278, 278, 278, 278, 889, 278, 278, 278, 278, 278, 278, 222, 611, 944, 611, 278, 278, 278, 278 ] } # The chracter #39 (single quote) in Times-* fonts has 333 width # in afm files. But Acrobat uses a width nallower than it. I use an # experimental value 222. sub fontwidth_Times_Bold { [ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, # 333, 555, 500, 500, 1000, 833, 333, 333, 333, 500, 570, 333, 555, 500, 500, 1000, 833, 222, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 500, 500, 167, 500, 500, 500, 500, 278, 500, 500, 333, 333, 556, 556, 250, 500, 500, 500, 250, 250, 540, 350, 333, 500, 500, 500, 1000, 1000, 250, 500, 250, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 1000, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 1000, 250, 300, 250, 250, 250, 250, 667, 778, 1000, 330, 250, 250, 250, 250, 250, 722, 250, 250, 250, 278, 250, 250, 278, 500, 722, 556, 250, 250, 250, 250 ] } sub fontwidth_Times_BoldItalic { [ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, # 389, 555, 500, 500, 833, 778, 333, 333, 333, 500, 570, 389, 555, 500, 500, 833, 778, 222, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722, 611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500, 333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500, 500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 389, 500, 500, 167, 500, 500, 500, 500, 278, 500, 500, 333, 333, 556, 556, 250, 500, 500, 500, 250, 250, 500, 350, 333, 500, 500, 500, 1000, 1000, 250, 500, 250, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 1000, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 944, 250, 266, 250, 250, 250, 250, 611, 722, 944, 300, 250, 250, 250, 250, 250, 722, 250, 250, 250, 278, 250, 250, 278, 500, 722, 500, 250, 250, 250, 250 ] } sub fontwidth_Times_Italic { [ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, # 333, 420, 500, 500, 833, 778, 333, 333, 333, 500, 675, 333, 420, 500, 500, 833, 778, 222, 333, 333, 500, 675, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500, 920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722, 611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500, 333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500, 500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 389, 500, 500, 167, 500, 500, 500, 500, 214, 556, 500, 333, 333, 500, 500, 250, 500, 500, 500, 250, 250, 523, 350, 333, 556, 556, 500, 889, 1000, 250, 500, 250, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 889, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 889, 250, 276, 250, 250, 250, 250, 556, 722, 944, 310, 250, 250, 250, 250, 250, 667, 250, 250, 250, 278, 250, 250, 278, 500, 667, 500, 250, 250, 250, 250 ] } sub fontwidth_Times_Roman { [ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, # 333, 408, 500, 500, 833, 778, 333, 333, 333, 500, 564, 333, 408, 500, 500, 833, 778, 222, 333, 333, 500, 564, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 500, 500, 167, 500, 500, 500, 500, 180, 444, 500, 333, 333, 556, 556, 250, 500, 500, 500, 250, 250, 453, 350, 333, 444, 444, 500, 1000, 1000, 250, 444, 250, 333, 333, 333, 333, 333, 333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 1000, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 889, 250, 276, 250, 250, 250, 250, 611, 722, 889, 310, 250, 250, 250, 250, 250, 667, 250, 250, 250, 278, 250, 250, 278, 500, 722, 500, 250, 250, 250, 250 ] }