PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/bin
Viewing File: /usr/share/texlive/texmf-dist/scripts/thumbpdf/thumbpdf.pl
eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' if 0; use strict; $^W=1; # turn warning on # # thumbpdf.pl # # Copyright (C) 1999-2012 Heiko Oberdiek. # # This work may be distributed and/or modified under the # conditions of the LaTeX Project Public License, either version 1.3 # of this license or (at your option) any later version. # The latest version of this license is in # http://www.latex-project.org/lppl.txt # and version 1.3 or later is part of all distributions of LaTeX # version 2003/12/01 or later. # # This work has the LPPL maintenance status "maintained". # # This Current Maintainer of this work is Heiko Oberdiek. # # See file "readme.txt" for a list of files that belong to this project. # # This file "thumbpdf.pl" may be renamed to "thumbpdf" # for installation purposes. # my $file = "thumbpdf.pl"; my $program = uc($&) if $file =~ /^\w+/; my $prj = 'thumbpdf'; my $version = "3.15"; my $date = "2012/04/18"; my $author = "Heiko Oberdiek"; my $copyright = "Copyright (c) 1999-2012 by $author."; # # Reqirements: Perl5, Ghostscript # History: # 1999/02/14 v1.0: First release. # 1999/02/23 v1.1: # * Looking for the media box to calculate the resolution # for Ghostscript # * new option --resolution # 1999/03/01 v1.2: # * optimization: indirect objects for length values removed. # * "first line" from epstopdf # 1999/03/12 v1.3: # * Copyright: LPPL # 1999/05/05 v1.4: # * Detecting of cygwin32 environment. # * Minor corrections of output of error messages. # * Sharing RGB objects. # 1999/06/13 v1.5: # * gs detection extended. # 1999/07/27 v1.6 # 1999/08/08 v1.7: # * \immediate before \pdfobj (pdfTeX 0.14a) # 1999/09/09 v1.8 # 1999/09/06 v1.9: # * Check for direct /Length values (for jpg images) # 2000/01/11 v1.10: # * Bug fix: /Length (direct) as last entry. # * Direct /Length in RGB objects supported. # 2000/01/19 v1.11: # * "for (my $j=0;...;...)" replaced by "my $j; for($j=0;...;...)", # because there exist perl versions that have problems with. # 2000/02/11 v1.12: # * Option `clean' added. # * The name of thumbnail data file: jobname.tnd, # if thumbpdf is called: thumbpdf jobname[.pdf] [options] # 2000/02/22 v2.0: # * pdfmark support for dvips/ps2pdf route added. # * <jobname>.tpt replaces thumbdta.tex (for pdfTeX) # <jobname>.tpm (for pdfmark) # <jobname>.top replaces thumbopt.tex # * Options `useps', `modes' added, # `makedef' renamed to `makedata'. # 2000/02/28 v2.1: # * Environment variable `THUMBPDF' supported. # 2000/03/07 v2.2: # * Support for Distiller 3 and 4, the streams are uncompressed. # * Call of gs is changed in order to show the currently processed # page number of the pdf file. # * Option --printgscmd creates the command line file `thumbpdf.gs' # for the Ghostscript call. # 2000/03/22 v2.3: # * Bug fix: --useps now works. # 2000/04/10 v2.4: # * Fix for ActiveState Perl 5.6.0: uc line changed, fork removed. # (Thanks to Andreas Buehmann <andreas.buehmann@gmx.de>.) # * Version test for thumbpdf.tex added for users that # mix versions, sigh. # 2000/07/29 v2.5: # * `save' trick in call of ghostscript. # * Undocumented option --gspages added. # 2000/09/27 v2.6 # 2000/10/27 v2.7: # * -dFIXEDMEDIA=0 added in gs call # 2001/01/12 v2.8: # * Bug fix in dvips mode and active option `level2': # pack parameter corrected for little-endian machines. # * /Rotate in pdf pages: # ghostscript versions around 6.01 have added a hack # in /pdfshowpage_setpage, that ignores the /Rotate entry. # A patch is added to disable the hack. # * Ghostscript uses the MediaBox for calculating the # page size. For version 6.50 a patch is added to use # the /CropBox instead. # 2001/03/29 v2.9: # * Option --password added. # 2001/04/02 v2.10 # 2001/04/26 v2.11 # * Option --antialias added (suggestion of Juergen Bausa). # 2002/01/11 v3.0 # * Syntax of option --antialias changed (see readme.txt). # * Support for VTeX's PS mode added. # * Greek mode added (see readme.txt). # * Signal handlers added for cleanup. # 2002/05/26 v3.1 # * SIG_HUP unkown in Windows. # * Bug fix: The signal function for __DIE__ "cleanup" aborts # before the error message of "die" is printed. # Replaced by "clean" that does not contain "exit 1". # * Small bug fix in mode detection and mode "vtex" # removed from list. "vtexpdfmark" was detected, # "vtex" did not work and perhaps it will be used # later for VTeX in PDF mode. # 2002/05/26 v3.2 # * Fix: "MacOS/X: darwin" is now not interpreted as # Windows. # 2003/03/19 v3.3 # * Fix for gs 8.00 in mode dvips: # THB_DistillerPatch also applied to ghostscript >= 8.00. # 2003/06/06 v3.4 # * Bug fix, two forgotten "pop"s added for Distiller case. # 2004/10/24 v3.5 # * Revert Cygwin detection: is unix (request by Jan Nieuwenhuizen). # * LPPL 1.3. # 2004/11/19 v3.6 # * Bug fix for dvips mode and gs < 8.00 (/stackunderflow in pop). # 2004/11/19 v3.7: # * For easier debugging, the special thumbpdf objects of # thumbpdf.pdf are now valid PDF objects (dictionaries). # * Remove of extra '\n' before "endstream" that is added # by pdfTeX 1.20a. # 2005/07/06 v3.8: # * Fix because of pdfTeX 1.30. # 2007/11/07 v3.9: # * Deprecation warning of perl 5.8.8 fixed. # 2008/04/16 v3.10 # 2010/07/07 v3.11 # * \input is used with file name extension for "thumbpdf.tex". # 2011/08/10 v3.13 # * Use gswin64c in Windows with 64 bits. # 2012/04/09 v3.14 # 2012/04/18 v3.15 # * Option --version added. # ### program identification my $title = "$program $version, $date - $copyright\n"; ### error strings my $Error = "!!! Error:"; # error prefix ### string constants for Ghostscript run # get Ghostscript command name my $GS = "gs"; $GS = "gs386" if $^O =~ /dos/i; $GS = "gsos2" if $^O =~ /os2/i; if ($^O =~ /mswin32c/i) { # http://perldoc.perl.org/perlport.html#DOS-and-Derivatives use Config; $GS = "gswin32c"; $GS = "gswin64c" if $Config{'archname'} =~ /mswin32-x64/i; } # Windows detection (no SIGHUP) my $Win = 0; $Win = 1 if $^O =~ /mswin32/i; my $gspages = 1; $gspages = 0 if $^O =~ /dos/i; ### variables my $jobname = ""; my $jobfile = ""; my $pdftexfile = ""; my $pdfmarkfile = ""; my $psext = ".ps"; my $pdfext = ".pdf"; my $pdftexext = ".tpt"; my $pdfmarkext = ".tpm"; my $thumbprefix = "thb"; my $envvar = "THUMBPDF"; my $pdffile = "thumbpdf.pdf"; my $logfile = "thumbpdf.log"; my $texfile = "thumbpdf.tex"; my $package = "thumbpdf.sty"; my $readme = "readme.txt"; my $gscnffile = "thumbpdf.gs"; my $gssection = "section I. `Known Problems'"; my @cleanlist = (); my $resolution = 9; my $mode_pdftex = 0; my $mode_pdfmark = 0; my $antialias_default = "4"; my @arglist = @ARGV; my $gskidrunning = 0; ### option variables my @bool = ("false", "true"); $::opt_device = "png16m"; $::opt_compress = "10"; $::opt_resolution = ""; $::opt_modes = "pdftex"; $::opt_gscmd = ""; $::opt_level2 = 0; $::opt_help = 0; $::opt_version = 0; $::opt_quiet = 0; $::opt_debug = 0; $::opt_verbose = 0; $::opt_useps = 0; $::opt_printgscmd = 0; $::opt_gspages = $gspages; # undocumented $::opt_makepng = 1; $::opt_makepdf = 1; $::opt_makedata = 1; $::opt_clean = 1; $::opt_password = ""; $::opt_antialias = $antialias_default; $::opt_greek = 0; my $usage = <<"END_OF_USAGE"; ${title}Syntax: \L$program\E [options] <jobname[.pdf|.ps]> Function: Support of thumbnails for pdfTeX or dvips/ps2pdf (pdfmark). Thumbnails are generated by Ghostscript and the result is written to data files for package `$package': `<jobname>$pdftexext' (pdfTeX), `<jobname>$pdfmarkext' (pdfmark) Options: (defaults:) --help print usage --version print version number --(no)quiet suppress messages ($bool[$::opt_quiet]) --(no)verbose verbose printing ($bool[$::opt_verbose]) --(no)debug debug informations ($bool[$::opt_debug]) --(no)makepng make thumbnails `$thumbprefix*.png' ($bool[$::opt_makepng]) --(no)makepdf make `$pdffile' with thumbnails as images ($bool[$::opt_makepdf]) --(no)makedata make data file(s) for package `$package' ($bool[$::opt_makedata]) --(no)clean clear temp files ($bool[$::opt_clean]) --(no)useps `makepng' uses `.ps' instead of `.pdf' file ($bool[$::opt_useps]) --(no)level2 `<jobname>.tpm' with ps level 2 features ($bool[$::opt_level2]) --(no)greek text in greek style (experimental) ($bool[$::opt_greek]) --gscmd <name> call of ghostscript ($GS) --antialias <num1>[num2] anti-aliasing, 0 = disable, 4 = max ($::opt_antialias) --device|png [png]<dev> Ghostscript device for thumbnails, dev = mono, gray, 16, 256, 16m ($::opt_device) --resolution <res> thumbnail resolution for makepng ($resolution) --compress <n> thumbnail compress level, n = 0..10 ($::opt_compress) --modes <mode>[,mode] mode=pdftex|pdfmark|dvips|ps2pdf| vtexpdfmark|all ($::opt_modes) --password <password> for an encrypted pdf file ($::opt_password) END_OF_USAGE ### environment variable THUMBPDF if ($ENV{$envvar}) { unshift(@ARGV, split(/\s+/, $ENV{$envvar})); } ### process options my @OrgArgv = @ARGV; use Getopt::Long; GetOptions( "help!", "version!", "quiet!", "debug!", "verbose!", "device|png=s", "gscmd=s", "level2!", "compress=i", "resolution=f", "modes=s", "useps!", "printgscmd!", "gspages!", "makepng!", "makepdf!", "makedata!", "clean!", "password=s", "antialias=s", "greek!" ) or die $usage; !$::opt_help or die $usage; if ($::opt_version) { print "$prj $date v$version\n"; exit(0); } @ARGV < 2 or die "$usage$Error Too many files!\n"; @ARGV == 1 or die "$usage$Error Missing jobname!\n"; $::opt_device = "png$::opt_device" unless $::opt_device =~ /^png/; $::opt_quiet = 0 if $::opt_verbose; $::opt_clean = 0 if $::opt_debug or !$::opt_makepdf or !$::opt_makedata; $::opt_compress = 0 if $::opt_compress < 0; $::opt_compress = 10 if $::opt_compress > 10; my $J = "^^J"; $J = "" if $::opt_compress == 10; $::opt_antialias = $antialias_default if $::opt_antialias eq ""; $::opt_antialias =~ /^[0124][0124]?$/ or die "$usage$Error Wrong value for option --antialias!\n"; $::opt_antialias .= $::opt_antialias if length($::opt_antialias) < 2; my $AntiAliasText = substr($::opt_antialias, 0, 1); my $AntiAliasGraphics = substr($::opt_antialias, 1, 1); $AntiAliasText = "1" if $AntiAliasText eq "0"; $AntiAliasGraphics = "1" if $AntiAliasGraphics eq "0"; $GS = $::opt_gscmd if $::opt_gscmd; $gspages = $::opt_gspages; ### get modes $::opt_modes = "\L$::opt_modes\E"; $::opt_modes =~ s/dvips/pdfmark/g; $::opt_modes =~ s/ps2pdf/pdfmark/g; $::opt_modes =~ s/vtexpdfmark/pdfmark/g; $::opt_modes =~ s/vtexpdfmark/pdfmark/g; if ($::opt_modes =~ /pdftex/) { $mode_pdftex = 1; $::opt_modes =~ s/pdftex//g; } if ($::opt_modes =~ /pdfmark/) { $mode_pdfmark = 1; $::opt_modes =~ s/pdfmark//g; } if ($::opt_modes =~ /all/) { $mode_pdftex = 1; $mode_pdfmark = 1; $::opt_modes =~ s/all//g; } $::opt_modes =~ s/\s+//g; $::opt_modes =~ s/,+/,/g; $::opt_modes =~ s/^,//; $::opt_modes =~ s/,$//; if ($::opt_modes ne "") { die "$usage$Error Unknown mode(s): `$::opt_modes'\n"; } if ($::opt_makedata) { $mode_pdftex or $mode_pdfmark or die "$usage$Error Missing mode!\n"; } ### get jobname $jobname = $ARGV[0]; if ($::opt_useps) { $jobname =~ s/\.ps$//i; $jobname =~ s/\\/\//g; $jobfile = $jobname . $psext; } else { $jobname =~ s/\.pdf$//i; $jobname =~ s/\\/\//g; $jobfile = $jobname . $pdfext; } $pdftexfile = $jobname . $pdftexext; $pdfmarkfile = $jobname . $pdfmarkext; print $title unless $::opt_quiet; print "* jobname: `$jobname'\n" if $::opt_verbose; if ($::opt_debug) { print <<"END_DEB"; * OSNAME: $^O * PERL_VERSION: $] * ARGV: @OrgArgv END_DEB } ### set signals $SIG{__DIE__} = \&clean; setsignals(\&cleanup); my $MaxThumb = 0; ### ### make thumbnails ### if ($::opt_makepng) { print "*** make png files / run Ghostscript ***\n" unless $::opt_quiet or $::opt_printgscmd; if ($::opt_useps) { print "* ps file: $jobfile\n" if $::opt_verbose; } else { print "* pdf file: $jobfile\n" if $::opt_verbose; } print "* Ghostscript command: `$GS'\n" . "* Ghostscript png device: `$::opt_device'\n" if $::opt_verbose; if ($::opt_resolution) { $resolution = $::opt_resolution } else { # looking for MediaBox my $max_x = 0; my $max_y = 0; { my $MB = $jobfile; open(MB, $MB) or die "$Error Cannot open `$MB'!\n"; binmode(MB); my $xy_patt = '[\-\.\d]'; while (<MB>) { if (/\/MediaBox\s*\[\s*($xy_patt+)\s+($xy_patt+)\s+($xy_patt+)\s+($xy_patt+)\s*\]/) { my $x = $3 - $1; my $y = $4 - $2; $max_x = $x if $x > $max_x; $max_y = $y if $y > $max_y; } } close(MB); } if ($max_x <= 0 || $max_y <= 0) { print "!!! Warning: MediaBox not found, " . "using default resolution: $resolution DPI\n"; } else { print "* Max. Size of MediaBox: $max_x x $max_y\n" if $::opt_verbose; my $rx = 106 * 72 / $max_x; my $ry = 106 * 72 / $max_y; $resolution = $rx; $resolution = $ry if $ry < $rx; print "* Resolution: $resolution DPI\n" if $::opt_verbose; } } # Ghostscript's pdfshowpage_setpage is patched for solving # some problems: # * gs6.0* includes a hack that ignores the /Rotate entry # in the PDF page, if OutputFile is set. # gs6.50 does not need a fix and it is not applied, # because pdfshowpage_setpage does not contain /OutputFile. # * If /CropBox is set, then it should be used instead # of the /MediaBox entry. Because the CropBox area should # be part of the MediaBox, the MediaBox is overwritten # with the CropBox values for generating the thumbnails. # The fixes are only applied for versions >= 6.0, because # gs5.50 gets a /PageSize problem with this fix. # my $SetPageHack = <<'SET_PAGE_HACK'; currentglobal true setglobal false /product where { pop product (Ghostscript) search { pop pop pop revision 600 ge { pop true } if }{pop} ifelse } if { /pdfdict where { pop pdfdict begin /pdfshowpage_setpage [ pdfdict /pdfshowpage_setpage get { dup type /nametype eq { dup /OutputFile eq { pop /AntiRotationHack }{ dup /MediaBox eq revision 650 ge and { /THB.CropHack { 1 index /CropBox pget { 2 index exch /MediaBox exch put } if } def /THB.CropHack cvx } if } ifelse } if } forall ] cvx def end } if } if setglobal SET_PAGE_HACK my $Greek = ""; $Greek = <<'END_GREEK' if $::opt_greek; currentglobal true setglobal userdict begin % * Patch for `show' /THB_ORG_show {show} bind def /THB_greekstring /.charboxpath where { pop { currentpoint newpath moveto true .charboxpath closepath fill } bind def }{ { { 1 string dup 0 4 -1 roll put dup stringwidth pop exch true charpath flattenpath pathbbox 2 index sub exch 3 index sub exch rectfill 0 rmoveto } forall } bind def } ifelse /show { currentfont /FontType get 1 eq { dup gsave % assuming white background [ currentrgbcolor ] { 1 add 2 div } forall setrgbcolor THB_greekstring grestore stringwidth pop 0 rmoveto }{ THB_ORG_show } ifelse } bind def % * Patch for the PDF case userdict /GS_PDF_ProcSet known { % GS_PDF_ProcSet is readonly, so it will be copied first GS_PDF_ProcSet length 10 add dict dup GS_PDF_ProcSet { put dup } forall /GS_PDF_ProcSet exch def dup begin % `setshowstate' contains the use of `show', so it has to % be overwritten, because it was defined with `bind'. % The definition is taken from `pdf_ops.ps'. revision 710 lt { % 5.50, 6.51, 7.00, 7.02 /setshowstate { WordSpacing 0 eq TextSpacing 0 eq and { TextRenderingMode 0 eq { { setfillstate show } } { { false charpath textrenderingprocs TextRenderingMode get exec } } ifelse } { TextRenderingMode 0 eq { WordSpacing 0 eq { { setfillstate TextSpacing exch 0 exch ashow } } { TextSpacing 0 eq { { setfillstate WordSpacing exch 0 exch 32 exch widthshow } } { { setfillstate WordSpacing exch TextSpacing exch 0 32 4 2 roll 0 exch awidthshow } } ifelse } ifelse } { { WordSpacing TextSpacing % Implement the combination of t3 and false charpath. % Note that we must use cshow for this, because we % can't parse multi-byte strings any other way. % Stack: string xword xchar { pop pop (x) dup 0 3 index put false charpath % Stack: xword xchar ccode 3 copy 32 eq { add } { exch pop } ifelse 0 rmoveto pop } 4 -1 roll cshow pop pop textrenderingprocs TextRenderingMode get exec } } ifelse } ifelse /Show gput } bdef }{ % 7.10 /setshowstate { WordSpacing 0 eq TextSpacing 0 eq and { TextRenderingMode 0 eq { { setfillstate show } } { { false charpath textrenderingprocs TextRenderingMode get exec } } ifelse } { TextRenderingMode 0 eq { WordSpacing 0 eq { { setfillstate TextSpacing 0 Vexch 3 -1 roll ashow } } { TextSpacing 0 eq { { setfillstate WordSpacing 0 Vexch 32 4 -1 roll widthshow } } { { setfillstate WordSpacing 0 Vexch 32 TextSpacing 0 Vexch 6 -1 roll awidthshow } } ifelse } ifelse } { { WordSpacing TextSpacing % Implement the combination of t3 and false charpath. % Note that we must use cshow for this, because we % can't parse multi-byte strings any other way. % Stack: string xword xchar { pop pop (x) dup 0 3 index put false charpath % Stack: xword xchar ccode 3 copy 32 eq { add } { exch pop } ifelse 0 Vexch rmoveto pop } 4 -1 roll cshow pop pop textrenderingprocs TextRenderingMode get exec } } ifelse } ifelse /Show gput } bdef } ifelse end readonly pop } if end setglobal END_GREEK my $SetPassword = ""; $SetPassword = "/PDFPassword($::opt_password)def" if $::opt_password; my $PSHeader = "save pop $SetPassword $SetPageHack $Greek"; $PSHeader =~ s/%\s.*\n/ /g; $PSHeader =~ s/\s+/ /g; $PSHeader =~ s/\s+([\(\/\[\]\{\}])/$1/g; $PSHeader =~ s/([\)\[\]\{\}])\s+/$1/g; $PSHeader =~ s/\s+$//; my $AntiAlias = ""; $AntiAlias = "\n-dTextAlphaBits=$AntiAliasText\n" . "-dGraphicsAlphaBits=$AntiAliasGraphics" if $::opt_antialias; my $gs_cmd = <<"GS_CMD_END"; $GS$AntiAlias -dNOPAUSE -dBATCH -sDEVICE=$::opt_device -r$resolution -sOutputFile=$thumbprefix%d.png -c "$PSHeader" -f $jobfile GS_CMD_END # The trick with `save' comes from `ps2pdf': # Doing an initial `save' helps keep fonts from being flushed # between pages. if ($::opt_printgscmd) { open(GSCNF, ">$gscnffile") or die "$Error Cannot open `$gscnffile'!\n"; $gs_cmd =~ s/^[^\r\n]+[\r\n]+//; print GSCNF $gs_cmd; close(GSCNF); my $options = "@arglist"; $options =~ s/\s*--?pr[intgscmd]*\s*/ /i; $options =~ s/^\s+//; $options =~ s/\s+$//; print <<"END_PERL" if $::opt_verbose; * Perl interpreter: $^X * Perl script: $0 END_PERL print <<"END_GS"; 1. Run `Ghostscript' manually: ==> $GS \@$gscnffile 2. Call `thumbpdf' again with the additional option `--nomakepng': ==> thumbpdf --nomakepng $options END_GS exit(0); } chomp($gs_cmd); $gs_cmd =~ s/\n/ /mg; print "> $gs_cmd\n" if $::opt_verbose; if ($::opt_debug) { if ($gspages) { print "* Ghostscript with page numbers\n"; } else { print "* Ghostscript without page numbers\n"; } } setsignals(\&gscleanup); my $capture = ""; if ($gspages) { my $newline = 0; open(KID, "$gs_cmd|") or die "$Error Cannot open Ghostscript ($!)!\n"; *::GSKID = *KID; $gskidrunning = 1; my $orgbar = $|; $|=1; while (<KID>) { $capture .= $_; if ($::opt_verbose) { print; } else { if (!$::opt_quiet) { print if /^Processing pages/; if (/^Page\s+(\d+)/) { print " " if $newline; $newline = 1; print "[$1]"; } } } } $gskidrunning =0; if (!close(KID)) { if ($!) { die "$Error Closing Ghostscript ($!)!\n"; } else { my $exitvalue = $? >> 8; die "$Error Closing Ghostscript (exit status: $exitvalue)!\n"; } } print "\n" if $newline; $| = $orgbar; } else # without pages { $capture = `$gs_cmd`; if (!defined($capture)) { die "$Error Cannot execute Ghostscript!\n"; } print $capture if $::opt_verbose; } if ($capture =~ /Error:\s*(.*)\n/) { die <<"END_DIE"; $Error `$1' (Ghostscript)! See `$readme', $gssection, for further information. END_DIE } if ($capture =~ /Unknown device:\s*(.*)\n/) { die "$Error Unknown device `$1' (Ghostscript)!\n"; } if ($?) { my $exitvalue = $?; if ($exitvalue > 255) { $exitvalue >>= 8; die "$Error Closing Ghostscript (exit status: $exitvalue)!\n"; } die "$Error Closing Ghostscript ($exitvalue)!\n"; } if ($capture =~ /Processing pages \d+ through (\d+)./) { $MaxThumb = $1; } print "* max. page: $MaxThumb\n" if $::opt_debug; setsignals(\&cleanup); } ### ### make thumbpdf.pdf file ### if ($::opt_makepdf) { print "*** make `$pdffile' / run pdfTeX ***\n" unless $::opt_quiet; if ($MaxThumb > 0) { my $i; for ($i=1; $i<=$MaxThumb; $i++) { push(@cleanlist, "$thumbprefix$i.png"); } } else { # get max thumb number to speed up the pdfTeX run $MaxThumb = 0; foreach (glob("$thumbprefix*.png")) { next unless /$thumbprefix(\d+).png/; $MaxThumb = $1 if $1 > $MaxThumb; push(@cleanlist, $_); } } push(@cleanlist, $logfile); push(@cleanlist, $pdffile); my $compress = $::opt_compress; $compress = 9 if $::opt_compress == 10; my $cmd = "pdftex \"" . "\\nonstopmode" . "\\pdfcompresslevel$compress" . "\\def\\thumbjob{$jobname}" . "\\def\\thumbmax{$MaxThumb}" . "\\input $texfile" . "\""; print "> $cmd\n" if $::opt_verbose; my @capture = `$cmd`; if (!@capture) { die "$Error Cannot execute pdfTeX!\n"; } if ($::opt_verbose) { print @capture; } else { foreach (@capture) { print if /^!\s/; } } if ($?) { my $exitvalue = $?; if ($exitvalue > 255) { $exitvalue >>= 8; die "$Error Closing pdfTeX (exit status: $exitvalue)!\n"; } die "$Error Closing pdfTeX ($exitvalue)!\n"; } # test version my $versionfound = 0; foreach (@capture) { if (/File:.*thumbpdf.*(\d\d\d\d\/\d\d\/\d\d)\s+v(\d+\.\d+)/) { $versionfound = 1; if ($1 ne $date or $2 ne $version) { print <<"END_WARN"; !!! Warning: Version of `thumbpdf.tex' does not match with perl script! Current `thumbpdf.tex': $1 v$2 Please install version: $date v$version END_WARN } } } print "!!! Warning: Version of `thumbpdf.tex' not found!\n" if !$versionfound; $_ = pop(@cleanlist); } ### ### parse thumbpdf.pdf ### if ($::opt_makedata) { push(@cleanlist, $pdffile); print "*** parse `$pdffile' ***\n" unless $::opt_quiet; ### reading file and parse obj structure my @objno = (); # obj number my @objdict = (); # boolean, object is dict my @objtext = (); # text of object my @objstream = (); # stream of object if any my $maxobj = 0; my @getobjindex = (); # $getobj[obj number] ==> index for $obj...[index] # open file my $PDF = $pdffile; open(PDF, $PDF) or die "$Error Cannot open `$PDF'!\n"; binmode(PDF); my $lineno = 0; # read header $_ = <PDF>; $lineno++; $_ or die "$Error Cannot read header of `$PDF' or file is empty!\n"; /^%PDF/ or die "$Error No PDF specification found!\n"; print "* pdf header: $_" if ($::opt_debug); # read body objects my $count = 0; while (<PDF>) { $lineno++; # continue, if comment line (2nd line of PDF output by pdfTeX 1.30) next if /^%/; # stop at xref last if /^xref$/; # scan first obj line /^(\d+)\s+0\s+obj\s*(<<)?$/ or die "$Error `obj' expected on line $lineno!\n"; $objno[$count] = $1; $getobjindex[$1] = $count; $objdict[$count] = ($2); # boolean (if $2 exists) my $stream = 0; print "* obj $objno[$count]" . (($objdict[$count]) ? " (dict)" : "") . "\n" if $::opt_debug; # get obj $objtext[$count] = ""; while (<PDF>) { $lineno++; if ($objdict[$count]) { if (/^>>/) { last if /^>>\s+endobj$/; # obj without stream # get stream $_ = <PDF>; $lineno++; /^stream$/ or die "$Error `stream' expected on line $lineno!\n"; print "* stream\n" if $::opt_debug; $objstream[$count] = ""; while (<PDF>) { $lineno++; if (/(.*)endstream$/) { $objstream[$count] .= $1; last; } $objstream[$count] .= $_; } $_ = <PDF>; $lineno++; /^endobj$/ or die "$Error `endobj' expected on line $lineno!\n"; last; } } else # no dict { last if /^endobj$/; } $objtext[$count] .= $_; } $count++; } close(PDF); $maxobj = $count; print "* $maxobj objects found.\n" if $::opt_debug; ### get thumbnail page numbers my @thumbpageno = (); my $found = 0; foreach (@objtext) { if (/^<<\/ListThumbs\s+(.+)>>$/) { $_ = $1; chomp; @thumbpageno = split / /; # split(/ /, $_); print "* ListThumbs: @thumbpageno\n" if $::opt_debug; $found = 1; last; } } $found or die "$Error `/ListThumbs' not found!\n"; { my $j; for ($j=0; $j<@thumbpageno; $j++) { $thumbpageno[$j] = $1 if $thumbpageno[$j] =~ /^{(.+)}$/; } } ### identify thumb objects my @thumbobj = (); # index for @obj... with image stream my @thumblength = (); # stream length values my @thumbrgbobj = (); # index for @obj... with rgb stream my @thumbrgblength = (); # rgb stream length values my $maxthumb = 0; $count = 0; my $i; for ($i=0; $i<$maxobj; $i++) { if ($objtext[$i] =~ /^\/Type\s+\/XObject\n\/Subtype\s+\/Image\n/m) { $thumbobj[$count] = $i; $_ = $'; $objtext[$i] = $_; # check width and height /\/Width\s+(\d+)\n\/Height\s+(\d+)/m or die "$Error width/height of thumbnail not found!\n"; print "* Size: $1x$2\n" if $::opt_debug; print "==> Width ($1) " . "of thumbnail `$thumbpageno[$count]' " . "is larger than recommended (106).\n" if $1 > 106; print "==> Height ($2) " . "of thumbnail `$thumbpageno[$count]' " . "is larger than recommended (106).\n" if $2 > 106; # get stream length if (/\/Length\s+(\d+)\s+([\/\>]|$)/m) { $thumblength[$count] = $1; print "* Length (direct): $1\n" if $::opt_debug; # remove whitespace after length obj $objtext[$i] =~ s/(\/Length\s+\d+)\s+\n/$1\n/; } else # looking for indirect reference { /\/Length\s+(\d+)\s+0\s+R/m or die "$Error `/Length' entry not found!\n"; # save obj text for later correction my $objpre = $`; my $objpost = $'; # look for length obj $getobjindex[$1] or die "$Error Length obj not found!\n"; $objtext[$getobjindex[$1]] =~ /^(\d+)$/m or die "$Error length value not found!\n"; $thumblength[$count] = $1; print "* Length (indirect): $1\n" if $::opt_debug; # insert obj length directly: $objtext[$i] = $objpre . "/Length $1" . $objpost; } # remove \n from end of stream if ($thumblength[$count] < length($objstream[$i])) { chop($objstream[$i]); } # check /Indexed /DeviceRGB if ($objtext[$i] =~ /\/ColorSpace\s+\[\/Indexed\s+\/DeviceRGB\s+(\d+)\s+(\d+)\s+0\s+R\]/m) { # correct thumb object text $objtext[$i] = "$`/ColorSpace [/Indexed /DeviceRGB $1 \\the\\pdflastobj\\ 0 R]$'"; # get RGB obj number $getobjindex[$2] or die "$Error RGB object not found!\n"; $_ = $getobjindex[$2]; $thumbrgbobj[$count] = $_; # get stream length if ($objtext[$_] =~ /\/Length\s+(\d+)\s+([\/\>]|$)/m) { $thumbrgblength[$count] = $1; print "* RGB length (direct): $1\n" if $::opt_debug; $objtext[$_] =~ s/(\/Length\s+\d+)\s+\n/$1\n/; } else # looking for indirect reference { $objtext[$_] =~ /\/Length\s+(\d+)\s+0\s+R/m or die "$Error Length entry of rgb object not found\n"; # save obj text for later correction my $objrgbpre = $`; my $objrgbpost = $'; # get rgb stream length $getobjindex[$1] or die "$Error RGB length object not found!\n"; $objtext[$getobjindex[$1]] =~ /^(\d+)$/m or die "$Error length value not found!\n"; $thumbrgblength[$count] = $1; print "* RGB length (indirect): $1\n" if $::opt_debug; # insert RGB object length directly: $objtext[$_] = $objrgbpre . "/Length $1" . $objrgbpost; } } $count++; } } $maxthumb = $count; if ($maxthumb != @thumbpageno) { my $pagecount = @thumbpageno; die "$Error $maxthumb thumbnails found, but there should be $pagecount!\n"; } print "* $maxthumb thumbnails found.\n" if $::opt_verbose; ### ### write data files ### my $timestamp; { my ($sec, $min, $hour, $mday, $mon, $year) = localtime(); $mon++; $year += 1900; $timestamp = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec); } if ($mode_pdftex) { print "*** write `$pdftexfile' (pdfTeX thumbnail data) ***\n" unless $::opt_quiet; my $DTA_PT = $pdftexfile; open(DTA_PT, ">$DTA_PT") or die "$Error Cannot open `$DTA_PT'!\n"; binmode(DTA_PT); print DTA_PT <<"END_DTA"; % File: $DTA_PT % Producer: $program $version % Mode: pdftex % Date: $timestamp END_DTA } my $maxpagethumb; my $dictbegin; if ($mode_pdfmark) { print "*** write `$pdfmarkfile' (pdfmark thumbnail data) ***\n" unless $::opt_quiet; my $DTA_PM = $pdfmarkfile; open(DTA_PM, ">$DTA_PM") or die "$Error Cannot open `$DTA_PM'!\n"; binmode(DTA_PM); # write tex part print DTA_PM <<"END_DTA"; % \\iffalse % File: $DTA_PM % Producer: $program $version % Mode: pdfmark % Date: $timestamp % \\fi END_DTA # looking for max. number of regular thumbnails $maxpagethumb = $maxthumb; for ($i=0; $i<$maxobj; $i++) { if ($objtext[$i] =~ /<<\/MaxThumbNumber\s+(\d+)>>/) { $maxpagethumb = $1; last; } } # write TeX part for ($i=$maxpagethumb; $i<$maxthumb; $i++) { print DTA_PM "% \\DefThisThumb{$thumbpageno[$i]}\n"; } # write PostScript header my $dictstart = <<'END_DICT'; 4 dict begin /enddict { counttomark 2 idiv dup dict begin {def} repeat pop currentdict end } bind def END_DICT chomp($dictstart); $dictbegin = "["; my $dictend = "enddict"; my $filter = ""; my $read = "readhexstring"; if ($::opt_level2) { $dictstart = " 3 dict begin"; $dictbegin = "<<"; $dictend = ">>"; $filter = " /ASCII85Decode filter"; $read = "readstring"; } print DTA_PM <<"END_DTA"; % \\endinput % TeX part ends here % PostScript definitions END_DTA my $PS_Header = <<"END_HEAD"; % % Default definition of pdfmark /pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse % % Check the version of Ghostscript. If it is below 6.0, % the commands, that produce the thumbnails, are defined % to be dummies. % true /product where { pop product (Ghostscript) search { pop pop pop revision 600 lt { (!!! Warning (thumbpdf): Ghostscript 6.0 required for thumbnails!\\n) print pop false } if }{pop} ifelse } if { % Syntax: <thumb object> thisTHB - % thisTHB is used globally, so it is defined in the current % dictionary (perhaps userdict should explicitly be set). /thisTHB {[ exch /Thumb exch /PAGE pdfmark} bind def $dictstart % Syntax: <page object> <thumb object> pagethumb - /pagethumb { [ 3 1 roll $dictbegin exch /Thumb exch $dictend /PUT pdfmark } bind def % Syntax: <thumb object> <stream length> % <mark> <key value pairs> streamobj - % % Distiller ignores the compression of previously compressed % streams and uses its own settings. Therefore for Distiller % the streams are uncompressed. % Now ghostscript versions greater than 8 behave in the same way % as Distiller. Therefore detection for this versions is added. % The detection and patch for distiller require features of level 2 % and the filter /FlateDecode (level 3), that is used by pdfTeX's % compression. /THB_DistillerPatch false def /languagelevel where { pop languagelevel 2 ge { product (Distiller) search {pop pop pop true}{pop false} ifelse product (Ghostscript) search { pop pop pop revision 800 ge }{pop false} ifelse or { (FlateDecode) { pop % Syntax: <dict> THB_DistillerPatch <dict> false % <dict> THB_DistillerPatch <dict> <filter> /THB_DistillerPatch { dup /Filter known { dup dup /Filter get exch /Filter undef }{false} ifelse } bind def } (FlateDecode) /Filter resourceforall } if } if } if /streamobj { $dictend exch % Stack: <thumb> <dict> <length> 3 -1 roll dup % Stack: <dict> <length> <thumb> <thumb> [ /_objdef 3 -1 roll /type /stream /OBJ pdfmark % Stack: <dict> <length> <thumb> dup dup 5 -1 roll % Stack: <length> <thumb> <thumb> <thumb> <dict> THB_DistillerPatch % Stack: <length> <thumb> <thumb> <thumb> <dict> <filter/false> [ 4 -2 roll /PUT pdfmark % Stack: <length> <thumb> <thumb> <filter/false> [ 3 1 roll currentfile${filter} % Stack: <length> <thumb> [ <thumb> <filter/false> <file> 6 -1 roll string $read pop % Stack: <thumb> [ <thumb> <filter/false> <string> exch dup type /booleantype ne {true} if {filter} if % Stack: <thumb> [ <thumb> <file> /PUT pdfmark % Stack: <thumb> [ exch /CLOSE pdfmark } bind def }{ % Syntax: <thumb object> thisTHB - /thisTHB {pop} bind def 2 dict begin % Syntax: <page object> <thumb object> pagethumb - /pagethumb {pop pop} bind def % Syntax: <thumb object> <stream length> % <mark> <key value pairs> streamobj - /streamobj { cleartomark exch pop string currentfile${filter} exch $read pop pop } bind def } ifelse END_HEAD $PS_Header =~ s/%[^\r\n]*[\r\n]+//gm; print DTA_PM $PS_Header; print DTA_PM <<"END_DTA"; % adding thumbnails to pages END_DTA for ($i=0; $i<$maxpagethumb; $i++) { print DTA_PM <<"END_DTA"; {Page$thumbpageno[$i]} {THB$thumbpageno[$i]} pagethumb END_DTA } print DTA_PM "\n% thumbnail data\n"; } for ($i=0; $i<$maxthumb; $i++) { # rgb object if ($thumbrgbobj[$i]) { # find the same rgb object my $j; for ($j=0; $j<$i; $j++) { next unless $thumbrgbobj[$j]; next unless $objtext[$thumbrgbobj[$j]] eq $objtext[$thumbrgbobj[$i]]; next unless $objstream[$thumbrgbobj[$j]] eq $objstream[$thumbrgbobj[$i]]; last; } if ($j==$i) # not found { if ($mode_pdftex) { { my $rgbstream = pdftexstream($objstream[$thumbrgbobj[$i]]); my $dict = $objtext[$thumbrgbobj[$i]]; if ($::opt_compress == 10) { chomp($dict); $dict =~ s/\n([^\/])/^^J\n$1/mg; $dict =~ s/[ ]+\//\//mg; } else { $dict =~ s/\n/^^J\n/mg; } print DTA_PT <<"END_DTA"; \\immediate\\pdfobj{<<$J $dict>>$J stream^^J $rgbstream endstream} \\DefRGB{$i} END_DTA } } if ($mode_pdfmark) { { my $rgbstream = pdfmarkstream($objstream[$thumbrgbobj[$i]]); my $rgblength = $thumbrgblength[$i]; my $dict = $objtext[$thumbrgbobj[$i]]; $dict =~ s/\/Length\s+\d+\s*//; $dict =~ s/^\s+//; $dict =~ s/\s+$//; print DTA_PM <<"END_DTA"; {RGB_$i} $rgblength $dictbegin $dict streamobj $rgbstream END_DTA } } } else # $j with same rgb obj { $objtext[$thumbobj[$i]] =~ s/\\the\\pdflastobj/\\UseRGB{$j}/; print "* Reuses RGB object $j for $i\n" if $::opt_debug; } } # thumb object if ($mode_pdftex) { { my $dict = $objtext[$thumbobj[$i]]; if ($::opt_compress == 10) { chomp($dict); $dict =~ s/\n([^\/])/^^J\n$1/mg; $dict =~ s/[ ]+\//\//mg; $dict =~ s/[ ]+\[/\[/mg; } else { $dict =~ s/\n/^^J\n/mg; } my $stream = pdftexstream($objstream[$thumbobj[$i]]); print DTA_PT <<"END_DTA"; \\immediate\\pdfobj{<<$J $dict>>$J stream^^J $stream endstream} \\DefThumb{$thumbpageno[$i]} END_DTA } } if ($mode_pdfmark) { { my $stream = pdfmarkstream($objstream[$thumbobj[$i]]); my $length = $thumblength[$i]; my $dict = $objtext[$thumbobj[$i]]; $dict =~ s/\\the\\pdflastobj\\\s*\d+\s*R/{RGB_$i}/; $dict =~ s/\\UseRGB{(\d+)}\\\s*\d+\s*R/{RGB_$1}/; $dict =~ s/\/Length\s+\d+\s*//; $dict =~ s/^\s+//; $dict =~ s/\s+$//; my $thismarker = ""; $thismarker = "_", if $i >= $maxpagethumb; print DTA_PM <<"END_DTA"; {THB$thismarker$thumbpageno[$i]} $length $dictbegin $dict streamobj $stream END_DTA } } } if ($mode_pdftex) { print DTA_PT "\\endinput\n"; close(DTA_PT); } if ($mode_pdfmark) { print DTA_PM <<"END_DTA"; end % end of thumbnail data file END_DTA close(DTA_PM); } } sub pdftexstream { my $str = ""; my $mod = 0; foreach (split(//, $_[0])) { my $num = ord($_); if ($num == 13) { $str .= '\\/'; } elsif ($num < 32) { $str .= '^^' . chr($num + 64); } elsif ($num == 32) { $str .= '\\~'; } # space elsif ($num == 37) { $str .= '\\%'; } # percent elsif ($num == 92) { $str .= '\\\\'; } # backslash elsif ($num == 94) { $str .= '\\+'; } # caret elsif ($num == 123) { $str .= '\\{'; } # curly brace left elsif ($num == 125) { $str .= '\\}'; } # curly brace right else { $str .= $_; } $mod++; if ($mod == 26) { $mod = 0; $str .= "\n"; } } chomp $str; return $str; } sub pdfmarkstream { my $str; if ($::opt_level2) { my $s = $_[0]; my $len = length($s); $str = ""; my $i; for ($i=0; $i<$len-4; $i+=4) { $_ = ASCII85Encode(substr($s, $i, 4)); s/!!!!!/z/; $str .= $_; } my $r = $len % 4; if ($r) { $_ = substr($s, $i, $r) . "\000\000\000"; $_ = ASCII85Encode(substr($_, 0, 4)); $str .= substr($_, 0, $r+1); } $str =~ s/(.{60})/$1\n/g; chomp($str); $str .= "~>"; } else { $str = uc(unpack('H*', $_[0])); $str =~ s/(.{60})/$1\n/g; chomp($str); } return $str; } sub ASCII85Encode { my $val = unpack("N", $_[0]); my @c; $c[4] = $val % 85 + 33; $val = int($val/85); $c[3] = $val % 85 + 33; $val = int($val/85); $c[2] = $val % 85 + 33; $val = int($val/85); $c[1] = $val % 85 + 33; $c[0] = int($val/85) + 33; return pack("C*", @c); } sub setsignals { my $func = $_[0]; $SIG{'HUP'} = $func unless $Win; $SIG{'INT'} = $func; $SIG{'QUIT'} = $func; $SIG{'TERM'} = $func; } sub clean { if ($::opt_clean) { print "*** clear temp files ***\n" unless $::opt_quiet; foreach (@cleanlist) { unlink; } } } sub cleanup { print "\n" unless $::opt_quiet; clean(); exit 1; } sub gscleanup { print "\n" unless $::opt_quiet; clean(); close(::GSKID) if $gskidrunning; foreach (glob("$thumbprefix*.png")) { unlink; } exit 1; } clean(); print "*** ready. ***\n" unless $::opt_quiet; __END__