PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/xaviersmandi.in/www/wp-content/plugins/nextgen-gallery/lib/pel-0.9.9/src/
Upload File :
Current File : /home/xaviersmandi.in/www/wp-content/plugins/nextgen-gallery/lib/pel-0.9.9/src/PelIfd.php
<?php

/**
 * PEL: PHP Exif Library.
 * A library with support for reading and
 * writing all Exif headers in JPEG and TIFF images using PHP.
 *
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Martin Geisler.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program in the file COPYING; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */
namespace lsolesen\pel;

/**
 * Classes for dealing with Exif IFDs.
 *
 * @author Martin Geisler <mgeisler@users.sourceforge.net>
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public
 *          License (GPL)
 * @package PEL
 */

/**
 * Class representing an Image File Directory (IFD).
 *
 * {@link PelTiff TIFF data} is structured as a number of Image File
 * Directories, IFDs for short. Each IFD contains a number of {@link
 * PelEntry entries}, some data and finally a link to the next IFD.
 *
 * @author Martin Geisler <mgeisler@users.sourceforge.net>
 * @package PEL
 */
class PelIfd implements \IteratorAggregate, \ArrayAccess
{

    /**
     * Main image IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the IFD of the main image.
     */
    const IFD0 = 0;

    /**
     * Thumbnail image IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the IFD of the thumbnail image.
     */
    const IFD1 = 1;

    /**
     * Exif IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the Exif sub-IFD.
     */
    const EXIF = 2;

    /**
     * GPS IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the GPS sub-IFD.
     */
    const GPS = 3;

    /**
     * Interoperability IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the interoperability sub-IFD.
     */
    const INTEROPERABILITY = 4;

    /**
     * Canon Maker Notes IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_MAKER_NOTES = 5;

    /**
     * Canon Camera Settings IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_CAMERA_SETTINGS = 6;

    /**
     * Canon Shot Info IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_SHOT_INFO = 7;

    /**
     * Canon Shot Info IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_PANORAMA = 8;

    /**
     * Canon Shot Info IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_PICTURE_INFO = 9;

    /**
     * Canon Shot Info IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_FILE_INFO = 10;

    /**
     * Canon Shot Info IFD.
     *
     * Pass this to the constructor when creating an IFD which will be
     * the canon maker notes sub-IFD.
     */
    const CANON_CUSTOM_FUNCTIONS = 11;

    private $ifdTypes = [
        self::IFD0,
        self::IFD1,
        self::EXIF,
        self::GPS,
        self::INTEROPERABILITY,
        self::CANON_MAKER_NOTES,
        self::CANON_CAMERA_SETTINGS,
        self::CANON_SHOT_INFO,
        self::CANON_PANORAMA,
        self::CANON_PICTURE_INFO,
        self::CANON_FILE_INFO,
        self::CANON_CUSTOM_FUNCTIONS
    ];

    /**
     * The maker notes held by this directory.
     *
     * Stores information of the MakerNotes IFD.
     * Available and required keys are: parent, data, components and offset
     *
     * @var array
     */
    private $maker_notes = [];

    /**
     * The entries held by this directory.
     *
     * Each tag in the directory is represented by a {@link PelEntry}
     * object in this array.
     *
     * @var array
     */
    private $entries = [];

    /**
     * The type of this directory.
     *
     * Initialized in the constructor. Must be one of {@link IFD0},
     * {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link
     * INTEROPERABILITY}.
     *
     * @var int
     */
    private $type;

    /**
     * The next directory.
     *
     * This will be initialized in the constructor, or be left as null
     * if this is the last directory.
     *
     * @var PelIfd
     */
    private $next = null;

    /**
     * Sub-directories pointed to by this directory.
     *
     * This will be an array of ({@link PelTag}, {@link PelIfd}) pairs.
     *
     * @var array
     */
    private $sub = [];

    /**
     * The thumbnail data.
     *
     * This will be initialized in the constructor, or be left as null
     * if there are no thumbnail as part of this directory.
     *
     * @var PelDataWindow
     */
    private $thumb_data = null;
    // TODO: use this format to choose between the
    // JPEG_INTERCHANGE_FORMAT and STRIP_OFFSETS tags.
    // private $thumb_format;

    /**
     * Construct a new Image File Directory (IFD).
     *
     * The IFD will be empty, use the {@link addEntry()} method to add
     * an {@link PelEntry}. Use the {@link setNext()} method to link
     * this IFD to another.
     *
     * @param
     *            int type the type of this IFD. Must be one of {@link
     *            IFD0}, {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link
     *            INTEROPERABILITY}. An {@link PelIfdException} will be thrown
     *            otherwise.
     */
    public function __construct($type)
    {
        if (!in_array($type, $this->ifdTypes)) {
            throw new PelIfdException('Unknown IFD type: %d', $type);
        }

        $this->type = $type;
    }

    /**
     * Stores Maker Notes data for an IFD (Probably PelIfd::EXIF only).
     *
     * @param PelIfd $parent
     *            the parent PelIfd of the current PelIfd
     *
     * @param PelDataWindow $data
     *            the data window that will provide the data.
     *
     * @param PelIfd $parent
     *            the components in the entry.
     *
     * @param int $offset
     *            the offset within the window where the directory will
     *            be found.
     */
    public function setMakerNotes($parent, $data, $components, $offset)
    {
        $this->maker_notes = [
            'parent' => $parent,
            'data' => $data,
            'components' => $components,
            'offset' => $offset
        ];
    }

    /**
     * Returns the Maker Notes data for an IFD (Probably PelIfd::EXIF only).
     *
     * @return array The maker_notes of IDF
     */
    public function getMakerNotes()
    {
        return $this->maker_notes;
    }

    /**
     * Load data into a Image File Directory (IFD).
     *
     * @param PelDataWindow $d
     *            the data window that will provide the data.
     *
     * @param int $offset
     *            the offset within the window where the directory will
     *            be found.
     */
    public function load(PelDataWindow $d, $offset)
    {
        $starting_offset = $offset;

        $thumb_offset = 0;
        $thumb_length = 0;

        Pel::debug('Constructing IFD at offset %d from %d bytes...', $offset, $d->getSize());

        /* Read the number of entries */
        $n = $d->getShort($offset);
        Pel::debug('Loading %d entries...', $n);

        $offset += 2;

        /* Check if we have enough data. */
        if ($offset + 12 * $n > $d->getSize()) {
            $n = floor(($offset - $d->getSize()) / 12);
            Pel::maybeThrow(new PelIfdException('Adjusted to: %d.', $n));
        }

        for ($i = 0; $i < $n; $i ++) {
            // TODO: increment window start instead of using offsets.
            $tag = $d->getShort($offset + 12 * $i);
            Pel::debug(
                'Loading entry with tag 0x%04X: %s (%d of %d)...',
                $tag,
                PelTag::getName($this->type, $tag),
                $i + 1,
                $n
            );

            switch ($tag) {
                case PelTag::EXIF_IFD_POINTER:
                case PelTag::GPS_INFO_IFD_POINTER:
                case PelTag::INTEROPERABILITY_IFD_POINTER:
                case PelTag::MAKER_NOTE:
                    $components = $d->getLong($offset + 12 * $i + 4);
                    $o = $d->getLong($offset + 12 * $i + 8);
                    Pel::debug('Found sub IFD at offset %d', $o);

                    /* Map tag to IFD type. */
                    if ($tag == PelTag::EXIF_IFD_POINTER) {
                        $type = PelIfd::EXIF;
                    } elseif ($tag == PelTag::GPS_INFO_IFD_POINTER) {
                        $type = PelIfd::GPS;
                    } elseif ($tag == PelTag::INTEROPERABILITY_IFD_POINTER) {
                        $type = PelIfd::INTEROPERABILITY;
                    } elseif ($tag == PelTag::MAKER_NOTE) {
                        // Store maker notes infos, because we need PelTag::MAKE of PelIfd::IFD0 for MakerNotes
                        // Thus MakerNotes will be loaded at the end of loading PelIfd::IFD0
                        $this->setMakerNotes($this, $d, $components, $o);
                        $this->loadSingleValue($d, $offset, $i, $tag);
                        break;
                    }

                    if ($starting_offset != $o) {
                        $ifd = new PelIfd($type);
                        try {
                            $ifd->load($d, $o);
                            $this->sub[$type] = $ifd;
                        } catch (PelDataWindowOffsetException $e) {
                            Pel::maybeThrow(new PelIfdException($e->getMessage()));
                        }
                    } else {
                        Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: %d, same as offset being loaded from.', $o));
                    }
                    break;
                case PelTag::JPEG_INTERCHANGE_FORMAT:
                    $thumb_offset = $d->getLong($offset + 12 * $i + 8);
                    $this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
                    break;
                case PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH:
                    $thumb_length = $d->getLong($offset + 12 * $i + 8);
                    $this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
                    break;
                default:
                    $this->loadSingleValue($d, $offset, $i, $tag);
                    break;
            }
        }

        /* Offset to next IFD */
        $o = $d->getLong($offset + 12 * $n);
        Pel::debug('Current offset is %d, link at %d points to %d.', $offset, $offset + 12 * $n, $o);

        if ($o > 0) {
            /* Sanity check: we need 6 bytes */
            if ($o > $d->getSize() - 6) {
                Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: ' . '%d > %d!', $o, $d->getSize() - 6));
            } else {
                if ($this->type == PelIfd::IFD1) {
                    // IFD1 shouldn't link further...
                    Pel::maybeThrow(new PelIfdException('IFD1 links to another IFD!'));
                }
                $this->next = new PelIfd(PelIfd::IFD1);
                $this->next->load($d, $o);
            }
        } else {
            Pel::debug('Last IFD.');
        }

        // Check if we finished loading IFD0 and EXIF IFD is set (EXIF IFD holds the MakerNotes)
        if ($this->type == PelIfd::IFD0 && isset($this->sub[PelIfd::EXIF])) {
            // Get MakerNotes from EXIF IFD and check if they are set
            $mk = $this->sub[PelIfd::EXIF]->getMakerNotes();
            if (!empty($mk) && count($mk) > 0) {
                // get Make tag and load maker notes if tag is valid
                $manufacturer = $this->getEntry(PelTag::MAKE);
                if ($manufacturer !== null) {
                    $manufacturer = $manufacturer->getValue();
                    $mkNotes = PelMakerNotes::createMakerNotesFromManufacturer($manufacturer, $mk['parent'], $mk['data'], $mk['components'], $mk['offset']);
                    if ($mkNotes !== null) {
                        // remove pre-loaded undefined MakerNotes
                        $mk['parent']->offsetUnset(PelTag::MAKER_NOTE);
                        $mkNotes->load();
                    }
                }
            }
        }
    }

    /**
     * Load a single value which didn't match any special {@link PelTag}.
     *
     * This method will add a single value given by the {@link PelDataWindow} and it's offset ($offset) and element counter ($i).
     *
     * Please note that the data you pass to this method should come
     * from an image, that is, it should be raw bytes. If instead you
     * want to create an entry for holding, say, an short integer, then
     * create a {@link PelEntryShort} object directly and load the data
     * into it.
     *
     * @param PelDataWindow $d
     *            the data window that will provide the data.
     *
     * @param integer $offset
     *            the offset within the window where the directory will
     *            be found.
     *
     * @param int $i
     *            the element's position in the {@link PelDataWindow} $d.
     *
     * @param int $tag
     *            the tag of the entry as defined in {@link PelTag}.
     */
    public function loadSingleValue($d, $offset, $i, $tag)
    {
        $format = $d->getShort($offset + 12 * $i + 2);
        $components = $d->getLong($offset + 12 * $i + 4);
        $size = PelFormat::getSize($format);
        if (is_string($size)) {
            Pel::maybeThrow(new PelException('Invalid format %s', $format));
            return;
        }

        try {
            /*
             * The data size. If bigger than 4 bytes, the actual data is
             * not in the entry but somewhere else, with the offset stored
             * in the entry.
             */
            $s = $size * $components;
            if ($s > 0) {
                $doff = $offset + 12 * $i + 8;
                if ($s > 4) {
                    $doff = $d->getLong($doff);
                }
                $data = $d->getClone($doff, $s);
            } else {
                $data = new PelDataWindow();
            }

            $entry = $this->newEntryFromData($tag, $format, $components, $data);
            $this->addEntry($entry);
        } catch (PelException $e) {
            /*
             * Throw the exception when running in strict mode, store
             * otherwise.
             */
            Pel::maybeThrow($e);
        }

        /* The format of the thumbnail is stored in this tag. */
        // TODO: handle TIFF thumbnail.
        // if ($tag == PelTag::COMPRESSION) {
        // $this->thumb_format = $data->getShort();
        // }
    }

    /**
     * Load a single value which didn't match any special {@link PelTag}.
     *
     * This method will add a single value given by the {@link PelDataWindow} and it's offset ($offset) and element counter ($i).
     *
     * Please note that the data you pass to this method should come
     * from an image, that is, it should be raw bytes. If instead you
     * want to create an entry for holding, say, an short integer, then
     * create a {@link PelEntryShort} object directly and load the data
     * into it.
     *
     * @param int $type
     *            the type of the ifd
     *
     * @param PelDataWindow $data
     *            the data window that will provide the data.
     *
     * @param integer $offset
     *            the offset within the window where the directory will
     *            be found.
     *
     * @param int $size
     *            the size in bytes of the maker notes section
     *
     * @param int $i
     *            the element's position in the {@link PelDataWindow} $data.
     *
     * @param int $format
     *            the format {@link PelFormat} of the entry.
     */
    public function loadSingleMakerNotesValue($type, $data, $offset, $size, $i, $format)
    {
        $elemSize = PelFormat::getSize($format);
        if ($size > 0) {
            $subdata = $data->getClone($offset + $i * $elemSize, $elemSize);
        } else {
            $subdata = new PelDataWindow();
        }

        try {
            $entry = $this->newEntryFromData($i + 1, $format, 1, $subdata);
            $this->addEntry($entry);
        } catch (PelException $e) {
            /*
            * Throw the exception when running in strict mode, store
            * otherwise.
            */
            Pel::maybeThrow($e);
        }

        /* The format of the thumbnail is stored in this tag. */
        // TODO: handle TIFF thumbnail.
        // if ($tag == PelTag::COMPRESSION) {
        // $this->thumb_format = $data->getShort();
        // }
    }

    /**
     * Make a new entry from a bunch of bytes.
     *
     * This method will create the proper subclass of {@link PelEntry}
     * corresponding to the {@link PelTag} and {@link PelFormat} given.
     * The entry will be initialized with the data given.
     *
     * Please note that the data you pass to this method should come
     * from an image, that is, it should be raw bytes. If instead you
     * want to create an entry for holding, say, an short integer, then
     * create a {@link PelEntryShort} object directly and load the data
     * into it.
     *
     * A {@link PelUnexpectedFormatException} is thrown if a mismatch is
     * discovered between the tag and format, and likewise a {@link
     * PelWrongComponentCountException} is thrown if the number of
     * components does not match the requirements of the tag. The
     * requirements for a given tag (if any) can be found in the
     * documentation for {@link PelTag}.
     *
     * @param integer $tag
     *            the tag of the entry as defined in {@link PelTag}.
     *
     * @param integer $format
     *            the format of the entry as defined in {@link PelFormat}.
     *
     * @param int $components
     *            the components in the entry.
     *
     * @param PelDataWindow $data
     *            the data which will be used to construct the
     *            entry.
     *
     * @return PelEntry a newly created entry, holding the data given.
     */
    public function newEntryFromData($tag, $format, $components, PelDataWindow $data)
    {

        /*
         * First handle tags for which we have a specific PelEntryXXX
         * class.
         */
        switch ($this->type) {
            case self::IFD0:
            case self::IFD1:
            case self::EXIF:
            case self::INTEROPERABILITY:
                switch ($tag) {
                    case PelTag::DATE_TIME:
                    case PelTag::DATE_TIME_ORIGINAL:
                    case PelTag::DATE_TIME_DIGITIZED:
                        if ($format != PelFormat::ASCII) {
                            throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::ASCII);
                        }
                        if ($components != 20) {
                            throw new PelWrongComponentCountException($this->type, $tag, $components, 20);
                        }
                        // TODO: handle timezones.
                        return new PelEntryTime($tag, $data->getBytes(0, - 1), PelEntryTime::EXIF_STRING);

                    case PelTag::COPYRIGHT:
                        if ($format != PelFormat::ASCII) {
                            throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::ASCII);
                        }
                        $v = explode("\0", trim($data->getBytes(), ' '));
                        if (! isset($v[1])) {
                            Pel::maybeThrow(new PelException('Invalid copyright: %s', $data->getBytes()));
                            // when not in strict mode, set empty copyright and continue
                            $v[1] = '';
                        }
                        return new PelEntryCopyright($v[0], $v[1]);

                    case PelTag::EXIF_VERSION:
                    case PelTag::FLASH_PIX_VERSION:
                    case PelTag::INTEROPERABILITY_VERSION:
                        if ($format != PelFormat::UNDEFINED) {
                            throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::UNDEFINED);
                        }
                        return new PelEntryVersion($tag, (float) $data->getBytes() / 100);

                    case PelTag::USER_COMMENT:
                        if ($format != PelFormat::UNDEFINED) {
                            throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::UNDEFINED);
                        }
                        if ($data->getSize() < 8) {
                            return new PelEntryUserComment();
                        } else {
                            return new PelEntryUserComment($data->getBytes(8), rtrim($data->getBytes(0, 8)));
                        }
                    // this point can not be reached
                    case PelTag::XP_TITLE:
                    case PelTag::XP_COMMENT:
                    case PelTag::XP_AUTHOR:
                    case PelTag::XP_KEYWORDS:
                    case PelTag::XP_SUBJECT:
                        if ($format != PelFormat::BYTE) {
                            throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::BYTE);
                        }
                        return new PelEntryWindowsString($tag, $data->getBytes(), true);
                }
            // This point can be reached! Continue with default.
            case self::GPS:
            default:
                /* Then handle the basic formats. */
                switch ($format) {
                    case PelFormat::BYTE:
                        $v = new PelEntryByte($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getByte($i));
                        }
                        return $v;

                    case PelFormat::SBYTE:
                        $v = new PelEntrySByte($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getSByte($i));
                        }
                        return $v;

                    case PelFormat::ASCII:
                        // cut off string after the first nul byte
                        $canonicalString = strstr($data->getBytes(0), "\0", true);
                        if ($canonicalString !== false) {
                            return new PelEntryAscii($tag, $canonicalString);
                        }
                        // TODO throw exception if string isn't nul-terminated
                        return new PelEntryAscii($tag, $data->getBytes(0));

                    case PelFormat::SHORT:
                        $v = new PelEntryShort($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getShort($i * 2));
                        }
                        return $v;

                    case PelFormat::SSHORT:
                        $v = new PelEntrySShort($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getSShort($i * 2));
                        }
                        return $v;

                    case PelFormat::LONG:
                        $v = new PelEntryLong($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getLong($i * 4));
                        }
                        return $v;

                    case PelFormat::SLONG:
                        $v = new PelEntrySLong($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getSLong($i * 4));
                        }
                        return $v;

                    case PelFormat::RATIONAL:
                        $v = new PelEntryRational($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getRational($i * 8));
                        }
                        return $v;

                    case PelFormat::SRATIONAL:
                        $v = new PelEntrySRational($tag);
                        for ($i = 0; $i < $components; $i ++) {
                            $v->addNumber($data->getSRational($i * 8));
                        }
                        return $v;

                    case PelFormat::UNDEFINED:
                        return new PelEntryUndefined($tag, $data->getBytes());

                    default:
                        throw new PelException('Unsupported format: %s', PelFormat::getName($format));
                }
        }
    }

    /**
     * Extract thumbnail data safely.
     *
     * It is safe to call this method repeatedly with either the offset
     * or the length set to zero, since it requires both of these
     * arguments to be positive before the thumbnail is extracted.
     *
     * When both parameters are set it will check the length against the
     * available data and adjust as necessary. Only then is the
     * thumbnail data loaded.
     *
     * @param PelDataWindow $d
     *            the data from which the thumbnail will be
     *            extracted.
     *
     * @param int $offset
     *            the offset into the data.
     *
     * @param int $length
     *            the length of the thumbnail.
     */
    private function safeSetThumbnail(PelDataWindow $d, $offset, $length)
    {
        /*
         * Load the thumbnail if both the offset and the length is
         * available.
         */
        if ($offset > 0 && $length > 0) {
            /*
             * Some images have a broken length, so we try to carefully
             * check the length before we store the thumbnail.
             */
            if ($offset + $length > $d->getSize()) {
                Pel::maybeThrow(
                    new PelIfdException(
                        'Thumbnail length %d bytes ' . 'adjusted to %d bytes.',
                        $length,
                        $d->getSize() - $offset
                    )
                );
                $length = $d->getSize() - $offset;
            }

            /* Now set the thumbnail normally. */
            try {
                $this->setThumbnail($d->getClone($offset, $length));
            } catch (PelDataWindowWindowException $e) {
                Pel::maybeThrow(new PelIfdException($e->getMessage()));
            }
        }
    }

    /**
     * Set thumbnail data.
     *
     * Use this to embed an arbitrary JPEG image within this IFD. The
     * data will be checked to ensure that it has a proper {@link
     * PelJpegMarker::EOI} at the end. If not, then the length is
     * adjusted until one if found. An {@link PelIfdException} might be
     * thrown (depending on {@link Pel::$strict}) this case.
     *
     * @param PelDataWindow $d
     *            the thumbnail data.
     */
    public function setThumbnail(PelDataWindow $d)
    {
        $size = $d->getSize();
        /* Now move backwards until we find the EOI JPEG marker. */
        while ($d->getByte($size - 2) != 0xFF || $d->getByte($size - 1) != PelJpegMarker::EOI) {
            $size --;
        }

        if ($size != $d->getSize()) {
            Pel::maybeThrow(new PelIfdException('Decrementing thumbnail size ' . 'to %d bytes', $size));
        }
        $this->thumb_data = $d->getClone(0, $size);
    }

    /**
     * Get the type of this directory.
     *
     * @return int of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, {@link
     *         PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
     *         PelIfd::INTEROPERABILITY}.
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Is a given tag valid for this IFD?
     *
     * Different types of IFDs can contain different kinds of tags ---
     * the {@link IFD0} type, for example, cannot contain a {@link
     * PelTag::GPS_LONGITUDE} tag.
     *
     * A special exception is tags with values above 0xF000. They are
     * treated as private tags and will be allowed everywhere (use this
     * for testing or for implementing your own types of tags).
     *
     * @param int $tag
     *            the tag.
     *
     * @return boolean true if the tag is considered valid in this IFD,
     *         false otherwise.
     *
     * @see getValidTags()
     */
    public function isValidTag($tag)
    {
        return $tag > 0xF000 || in_array($tag, $this->getValidTags());
    }

    /**
     * Returns a list of valid tags for this IFD.
     *
     * @return array an array of {@link PelTag}s which are valid for
     *         this IFD.
     */
    public function getValidTags()
    {
        switch ($this->type) {
            case PelIfd::IFD0:
            case PelIfd::IFD1:
                return [
                    PelTag::IMAGE_WIDTH,
                    PelTag::IMAGE_LENGTH,
                    PelTag::BITS_PER_SAMPLE,
                    PelTag::COMPRESSION,
                    PelTag::PHOTOMETRIC_INTERPRETATION,
                    PelTag::DOCUMENT_NAME,
                    PelTag::IMAGE_DESCRIPTION,
                    PelTag::MAKE,
                    PelTag::MODEL,
                    PelTag::STRIP_OFFSETS,
                    PelTag::ORIENTATION,
                    PelTag::SAMPLES_PER_PIXEL,
                    PelTag::ROWS_PER_STRIP,
                    PelTag::STRIP_BYTE_COUNTS,
                    PelTag::X_RESOLUTION,
                    PelTag::Y_RESOLUTION,
                    PelTag::PLANAR_CONFIGURATION,
                    PelTag::RESOLUTION_UNIT,
                    PelTag::TRANSFER_FUNCTION,
                    PelTag::SOFTWARE,
                    PelTag::DATE_TIME,
                    PelTag::ARTIST,
                    PelTag::PREDICTOR,
                    PelTag::WHITE_POINT,
                    PelTag::PRIMARY_CHROMATICITIES,
                    PelTag::EXTRA_SAMPLES,
                    PelTag::SAMPLE_FORMAT,
                    PelTag::JPEG_INTERCHANGE_FORMAT,
                    PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH,
                    PelTag::YCBCR_COEFFICIENTS,
                    PelTag::YCBCR_SUB_SAMPLING,
                    PelTag::YCBCR_POSITIONING,
                    PelTag::REFERENCE_BLACK_WHITE,
                    PelTag::COPYRIGHT,
                    PelTag::EXIF_IFD_POINTER,
                    PelTag::GPS_INFO_IFD_POINTER,
                    PelTag::PRINT_IM,
                    PelTag::XP_TITLE,
                    PelTag::XP_COMMENT,
                    PelTag::XP_AUTHOR,
                    PelTag::XP_KEYWORDS,
                    PelTag::XP_SUBJECT,
                    PelTag::RATING,
                    PelTag::RATING_PERCENT,
                    PelTag::APPLICATION_NOTES
                ];

            case PelIfd::EXIF:
                return [
                    PelTag::EXPOSURE_TIME,
                    PelTag::FNUMBER,
                    PelTag::EXPOSURE_PROGRAM,
                    PelTag::SPECTRAL_SENSITIVITY,
                    PelTag::ISO_SPEED_RATINGS,
                    PelTag::OECF,
                    PelTag::EXIF_VERSION,
                    PelTag::DATE_TIME_ORIGINAL,
                    PelTag::DATE_TIME_DIGITIZED,
                    PelTag::OFFSET_TIME,
                    PelTag::OFFSET_TIME_ORIGINAL,
                    PelTag::OFFSET_TIME_DIGITIZED,
                    PelTag::COMPONENTS_CONFIGURATION,
                    PelTag::COMPRESSED_BITS_PER_PIXEL,
                    PelTag::SHUTTER_SPEED_VALUE,
                    PelTag::APERTURE_VALUE,
                    PelTag::BRIGHTNESS_VALUE,
                    PelTag::EXPOSURE_BIAS_VALUE,
                    PelTag::MAX_APERTURE_VALUE,
                    PelTag::SUBJECT_DISTANCE,
                    PelTag::METERING_MODE,
                    PelTag::LIGHT_SOURCE,
                    PelTag::FLASH,
                    PelTag::FOCAL_LENGTH,
                    PelTag::MAKER_NOTE,
                    PelTag::USER_COMMENT,
                    PelTag::SUB_SEC_TIME,
                    PelTag::SUB_SEC_TIME_ORIGINAL,
                    PelTag::SUB_SEC_TIME_DIGITIZED,
                    PelTag::FLASH_PIX_VERSION,
                    PelTag::COLOR_SPACE,
                    PelTag::PIXEL_X_DIMENSION,
                    PelTag::PIXEL_Y_DIMENSION,
                    PelTag::RELATED_SOUND_FILE,
                    PelTag::FLASH_ENERGY,
                    PelTag::SPATIAL_FREQUENCY_RESPONSE,
                    PelTag::FOCAL_PLANE_X_RESOLUTION,
                    PelTag::FOCAL_PLANE_Y_RESOLUTION,
                    PelTag::FOCAL_PLANE_RESOLUTION_UNIT,
                    PelTag::SUBJECT_LOCATION,
                    PelTag::EXPOSURE_INDEX,
                    PelTag::SENSING_METHOD,
                    PelTag::FILE_SOURCE,
                    PelTag::SCENE_TYPE,
                    PelTag::CFA_PATTERN,
                    PelTag::CUSTOM_RENDERED,
                    PelTag::EXPOSURE_MODE,
                    PelTag::WHITE_BALANCE,
                    PelTag::DIGITAL_ZOOM_RATIO,
                    PelTag::FOCAL_LENGTH_IN_35MM_FILM,
                    PelTag::SCENE_CAPTURE_TYPE,
                    PelTag::GAIN_CONTROL,
                    PelTag::CONTRAST,
                    PelTag::SATURATION,
                    PelTag::SHARPNESS,
                    PelTag::DEVICE_SETTING_DESCRIPTION,
                    PelTag::SUBJECT_DISTANCE_RANGE,
                    PelTag::IMAGE_UNIQUE_ID,
                    PelTag::INTEROPERABILITY_IFD_POINTER,
                    PelTag::GAMMA
                ];

            case PelIfd::GPS:
                return [
                    PelTag::GPS_VERSION_ID,
                    PelTag::GPS_LATITUDE_REF,
                    PelTag::GPS_LATITUDE,
                    PelTag::GPS_LONGITUDE_REF,
                    PelTag::GPS_LONGITUDE,
                    PelTag::GPS_ALTITUDE_REF,
                    PelTag::GPS_ALTITUDE,
                    PelTag::GPS_TIME_STAMP,
                    PelTag::GPS_SATELLITES,
                    PelTag::GPS_STATUS,
                    PelTag::GPS_MEASURE_MODE,
                    PelTag::GPS_DOP,
                    PelTag::GPS_SPEED_REF,
                    PelTag::GPS_SPEED,
                    PelTag::GPS_TRACK_REF,
                    PelTag::GPS_TRACK,
                    PelTag::GPS_IMG_DIRECTION_REF,
                    PelTag::GPS_IMG_DIRECTION,
                    PelTag::GPS_MAP_DATUM,
                    PelTag::GPS_DEST_LATITUDE_REF,
                    PelTag::GPS_DEST_LATITUDE,
                    PelTag::GPS_DEST_LONGITUDE_REF,
                    PelTag::GPS_DEST_LONGITUDE,
                    PelTag::GPS_DEST_BEARING_REF,
                    PelTag::GPS_DEST_BEARING,
                    PelTag::GPS_DEST_DISTANCE_REF,
                    PelTag::GPS_DEST_DISTANCE,
                    PelTag::GPS_PROCESSING_METHOD,
                    PelTag::GPS_AREA_INFORMATION,
                    PelTag::GPS_DATE_STAMP,
                    PelTag::GPS_DIFFERENTIAL
                ];

            case PelIfd::INTEROPERABILITY:
                return [
                    PelTag::INTEROPERABILITY_INDEX,
                    PelTag::INTEROPERABILITY_VERSION,
                    PelTag::RELATED_IMAGE_FILE_FORMAT,
                    PelTag::RELATED_IMAGE_WIDTH,
                    PelTag::RELATED_IMAGE_LENGTH
                ];
            case PelIfd::CANON_MAKER_NOTES:
                return [
                    PelTag::CANON_CAMERA_SETTINGS,
                    PelTag::CANON_FOCAL_LENGTH,
                    PelTag::CANON_SHOT_INFO,
                    PelTag::CANON_PANORAMA,
                    PelTag::CANON_IMAGE_TYPE,
                    PelTag::CANON_FIRMWARE_VERSION,
                    PelTag::CANON_FILE_NUMBER,
                    PelTag::CANON_OWNER_NAME,
                    PelTag::CANON_SERIAL_NUMBER,
                    PelTag::CANON_CAMERA_INFO,
                    PelTag::CANON_CUSTOM_FUNCTIONS,
                    PelTag::CANON_MODEL_ID,
                    PelTag::CANON_PICTURE_INFO,
                    PelTag::CANON_THUMBNAIL_IMAGE_VALID_AREA,
                    PelTag::CANON_SERIAL_NUMBER_FORMAT,
                    PelTag::CANON_SUPER_MACRO,
                    PelTag::CANON_FIRMWARE_REVISION,
                    PelTag::CANON_AF_INFO,
                    PelTag::CANON_ORIGINAL_DECISION_DATA_OFFSET,
                    PelTag::CANON_WHITE_BALANCE_TABLE,
                    PelTag::CANON_LENS_MODEL,
                    PelTag::CANON_INTERNAL_SERIAL_NUMBER,
                    PelTag::CANON_DUST_REMOVAL_DATA,
                    PelTag::CANON_CUSTOM_FUNCTIONS_2,
                    PelTag::CANON_PROCESSING_INFO,
                    PelTag::CANON_MEASURED_COLOR,
                    PelTag::CANON_COLOR_SPACE,
                    PelTag::CANON_VRD_OFFSET,
                    PelTag::CANON_SENSOR_INFO,
                    PelTag::CANON_COLOR_DATA
                ];
            case PelIfd::CANON_CAMERA_SETTINGS:
                return [
                    PelTag::CANON_CS_MACRO,
                    PelTag::CANON_CS_SELF_TIMER,
                    PelTag::CANON_CS_QUALITY,
                    PelTag::CANON_CS_FLASH_MODE,
                    PelTag::CANON_CS_DRIVE_MODE,
                    PelTag::CANON_CS_FOCUS_MODE,
                    PelTag::CANON_CS_RECORD_MODE,
                    PelTag::CANON_CS_IMAGE_SIZE,
                    PelTag::CANON_CS_EASY_MODE,
                    PelTag::CANON_CS_DIGITAL_ZOOM,
                    PelTag::CANON_CS_CONTRAST,
                    PelTag::CANON_CS_SATURATION,
                    PelTag::CANON_CS_SHARPNESS,
                    PelTag::CANON_CS_ISO_SPEED,
                    PelTag::CANON_CS_METERING_MODE,
                    PelTag::CANON_CS_FOCUS_TYPE,
                    PelTag::CANON_CS_AF_POINT,
                    PelTag::CANON_CS_EXPOSURE_PROGRAM,
                    PelTag::CANON_CS_LENS_TYPE,
                    PelTag::CANON_CS_LENS,
                    PelTag::CANON_CS_SHORT_FOCAL,
                    PelTag::CANON_CS_FOCAL_UNITS,
                    PelTag::CANON_CS_MAX_APERTURE,
                    PelTag::CANON_CS_MIN_APERTURE,
                    PelTag::CANON_CS_FLASH_ACTIVITY,
                    PelTag::CANON_CS_FLASH_DETAILS,
                    PelTag::CANON_CS_FOCUS_CONTINUOUS,
                    PelTag::CANON_CS_AE_SETTING,
                    PelTag::CANON_CS_IMAGE_STABILIZATION,
                    PelTag::CANON_CS_DISPLAY_APERTURE,
                    PelTag::CANON_CS_ZOOM_SOURCE_WIDTH,
                    PelTag::CANON_CS_ZOOM_TARGET_WIDTH,
                    PelTag::CANON_CS_SPOT_METERING_MODE,
                    PelTag::CANON_CS_PHOTO_EFFECT,
                    PelTag::CANON_CS_MANUAL_FLASH_OUTPUT,
                    PelTag::CANON_CS_COLOR_TONE,
                    PelTag::CANON_CS_SRAW_QUALITY
                ];
            case PelIfd::CANON_SHOT_INFO:
                return [
                    PelTag::CANON_SI_ISO_SPEED,
                    PelTag::CANON_SI_MEASURED_EV,
                    PelTag::CANON_SI_TARGET_APERTURE,
                    PelTag::CANON_SI_TARGET_SHUTTER_SPEED,
                    PelTag::CANON_SI_WHITE_BALANCE,
                    PelTag::CANON_SI_SLOW_SHUTTER,
                    PelTag::CANON_SI_SEQUENCE,
                    PelTag::CANON_SI_AF_POINT_USED,
                    PelTag::CANON_SI_FLASH_BIAS,
                    PelTag::CANON_SI_AUTO_EXPOSURE_BRACKETING,
                    PelTag::CANON_SI_SUBJECT_DISTANCE,
                    PelTag::CANON_SI_APERTURE_VALUE,
                    PelTag::CANON_SI_SHUTTER_SPEED_VALUE,
                    PelTag::CANON_SI_MEASURED_EV2,
                    PelTag::CANON_SI_CAMERA_TYPE,
                    PelTag::CANON_SI_AUTO_ROTATE,
                    PelTag::CANON_SI_ND_FILTER
                ];
            case PelIfd::CANON_PANORAMA:
                return [
                    PelTag::CANON_PA_PANORAMA_FRAME,
                    PelTag::CANON_PA_PANORAMA_DIRECTION
                ];
            case PelIfd::CANON_PICTURE_INFO:
                return [
                    PelTag::CANON_PI_IMAGE_WIDTH,
                    PelTag::CANON_PI_IMAGE_HEIGHT,
                    PelTag::CANON_PI_IMAGE_WIDTH_AS_SHOT,
                    PelTag::CANON_PI_IMAGE_HEIGHT_AS_SHOT,
                    PelTag::CANON_PI_AF_POINTS_USED,
                    PelTag::CANON_PI_AF_POINTS_USED_20D
                ];
            case PelIfd::CANON_FILE_INFO:
                return [
                    PelTag::CANON_FI_FILE_NUMBER,
                    PelTag::CANON_FI_BRACKET_MODE,
                    PelTag::CANON_FI_BRACKET_VALUE,
                    PelTag::CANON_FI_BRACKET_SHOT_NUMBER,
                    PelTag::CANON_FI_RAW_JPG_QUALITY,
                    PelTag::CANON_FI_RAW_JPG_SIZE,
                    PelTag::CANON_FI_NOISE_REDUCTION,
                    PelTag::CANON_FI_WB_BRACKET_MODE,
                    PelTag::CANON_FI_WB_BRACKET_VALUE_AB,
                    PelTag::CANON_FI_WB_BRACKET_VALUE_GM,
                    PelTag::CANON_FI_FILTER_EFFECT,
                    PelTag::CANON_FI_TONING_EFFECT,
                    PelTag::CANON_FI_MACRO_MAGNIFICATION,
                    PelTag::CANON_FI_LIVE_VIEW_SHOOTING,
                    PelTag::CANON_FI_FOCUS_DISTANCE_UPPER,
                    PelTag::CANON_FI_FOCUS_DISTANCE_LOWER,
                    PelTag::CANON_FI_FLASH_EXPOSURE_LOCK
                ];

            /*
             * TODO: Where do these tags belong?
             * PelTag::FILL_ORDER,
             * PelTag::TRANSFER_RANGE,
             * PelTag::JPEG_PROC,
             * PelTag::BATTERY_LEVEL,
             * PelTag::IPTC_NAA,
             * PelTag::INTER_COLOR_PROFILE,
             * PelTag::CFA_REPEAT_PATTERN_DIM,
             */
        }
    }

    /**
     * Get the name of an IFD type.
     *
     * @param int $type
     *            one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1},
     *            {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
     *            PelIfd::INTEROPERABILITY}.
     *
     * @return string the name of type.
     */
    public static function getTypeName($type)
    {
        switch ($type) {
            case self::IFD0:
                return '0';
            case self::IFD1:
                return '1';
            case self::EXIF:
                return 'Exif';
            case self::GPS:
                return 'GPS';
            case self::INTEROPERABILITY:
                return 'Interoperability';
            case self::CANON_MAKER_NOTES:
                return 'Canon Maker Notes';
            case self::CANON_CAMERA_SETTINGS:
                return 'Canon Camera Settings';
            case self::CANON_SHOT_INFO:
                return 'Canon Shot Information';
            case self::CANON_PANORAMA:
                return 'Canon Panorama Information';
            case self::CANON_PICTURE_INFO:
                return 'Canon Picture Information';
            case self::CANON_FILE_INFO:
                return 'Canon File Information';
            case self::CANON_CUSTOM_FUNCTIONS:
                return 'Canon Custom Functions';
            default:
                throw new PelIfdException('Unknown IFD type: %d', $type);
        }
    }

    /**
     * Get the name of this directory.
     *
     * @return string the name of this directory.
     */
    public function getName()
    {
        return $this->getTypeName($this->type);
    }

    /**
     * Adds an entry to the directory.
     *
     * @param PelEntry $e
     *            the entry that will be added. If the entry is not
     *            valid in this IFD (as per {@link isValidTag()}) an
     *            {@link PelInvalidDataException} is thrown.
     *
     * @todo The entry will be identified with its tag, so each
     *       directory can only contain one entry with each tag. Is this a
     *       bug?
     */
    public function addEntry(PelEntry $e)
    {
        if ($this->isValidTag($e->getTag())) {
            $e->setIfdType($this->type);
            $this->entries[$e->getTag()] = $e;
        } else {
            throw new PelInvalidDataException("IFD %s cannot hold\n%s", $this->getName(), $e->__toString());
        }
    }

    /**
     * Does a given tag exist in this IFD?
     *
     * This methods is part of the ArrayAccess SPL interface for
     * overriding array access of objects, it allows you to check for
     * existance of an entry in the IFD:
     *
     * <code>
     * if (isset($ifd[PelTag::FNUMBER]))
     * // ... do something with the F-number.
     * </code>
     *
     * @param int $tag
     *            the offset to check.
     *
     * @return boolean whether the tag exists.
     */
    public function offsetExists($tag)
    {
        return isset($this->entries[$tag]);
    }

    /**
     * Retrieve a given tag from this IFD.
     *
     * This methods is part of the ArrayAccess SPL interface for
     * overriding array access of objects, it allows you to read entries
     * from the IFD the same was as for an array:
     *
     * <code>
     * $entry = $ifd[PelTag::FNUMBER];
     * </code>
     *
     * @param int $tag
     *            the tag to return. It is an error to ask for a tag
     *            which is not in the IFD, just like asking for a non-existant
     *            array entry.
     *
     * @return PelEntry the entry.
     */
    public function offsetGet($tag)
    {
        return $this->entries[$tag];
    }

    /**
     * Set or update a given tag in this IFD.
     *
     * This methods is part of the ArrayAccess SPL interface for
     * overriding array access of objects, it allows you to add new
     * entries or replace esisting entries by doing:
     *
     * <code>
     * $ifd[PelTag::EXPOSURE_BIAS_VALUE] = $entry;
     * </code>
     *
     * Note that the actual array index passed is ignored! Instead the
     * {@link PelTag} from the entry is used.
     *
     * @param int $tag
     *            unused.
     *
     * @param PelEntry $e
     *            the new value.
     */
    public function offsetSet($tag, $e)
    {
        if ($e instanceof PelEntry) {
            $tag = $e->getTag();
            $this->entries[$tag] = $e;
        } else {
            throw new PelInvalidArgumentException('Argument "%s" must be a PelEntry.', $e);
        }
    }

    /**
     * Unset a given tag in this IFD.
     *
     * This methods is part of the ArrayAccess SPL interface for
     * overriding array access of objects, it allows you to delete
     * entries in the IFD by doing:
     *
     * <code>
     * unset($ifd[PelTag::EXPOSURE_BIAS_VALUE])
     * </code>
     *
     * @param int $tag
     *            the offset to delete.
     */
    public function offsetUnset($tag)
    {
        unset($this->entries[$tag]);
    }

    /**
     * Retrieve an entry.
     *
     * @param int $tag
     *            the tag identifying the entry.
     *
     * @return PelEntry the entry associated with the tag, or null if no
     *         such entry exists.
     */
    public function getEntry($tag)
    {
        if (isset($this->entries[$tag])) {
            return $this->entries[$tag];
        } else {
            return null;
        }
    }

    /**
     * Returns all entries contained in this IFD.
     *
     * @return array an array of {@link PelEntry} objects, or rather
     *         descendant classes. The array has {@link PelTag}s as keys
     *         and the entries as values.
     *
     * @see getEntry
     * @see getIterator
     */
    public function getEntries()
    {
        return $this->entries;
    }

    /**
     * Return an iterator for all entries contained in this IFD.
     *
     * Used with foreach as in
     *
     * <code>
     * foreach ($ifd as $tag => $entry) {
     * // $tag is now a PelTag and $entry is a PelEntry object.
     * }
     * </code>
     *
     * @return Iterator an iterator using the {@link PelTag tags} as
     *         keys and the entries as values.
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->entries);
    }

    /**
     * Returns available thumbnail data.
     *
     * @return string the bytes in the thumbnail, if any. If the IFD
     *         does not contain any thumbnail data, the empty string is
     *         returned.
     *
     * @todo Throw an exception instead when no data is available?
     *
     * @todo Return the $this->thumb_data object instead of the bytes?
     */
    public function getThumbnailData()
    {
        if ($this->thumb_data !== null) {
            return $this->thumb_data->getBytes();
        } else {
            return '';
        }
    }

    /**
     * Make this directory point to a new directory.
     *
     * @param PelIfd $i
     *            the IFD that this directory will point to.
     */
    public function setNextIfd(PelIfd $i)
    {
        $this->next = $i;
    }

    /**
     * Return the IFD pointed to by this directory.
     *
     * @return PelIfd the next IFD, following this IFD. If this is the
     *         last IFD, null is returned.
     */
    public function getNextIfd()
    {
        return $this->next;
    }

    /**
     * Check if this is the last IFD.
     *
     * @return boolean true if there are no following IFD, false
     *         otherwise.
     */
    public function isLastIfd()
    {
        return $this->next === null;
    }

    /**
     * Add a sub-IFD.
     *
     * Any previous sub-IFD of the same type will be overwritten.
     *
     * @param PelIfd $sub
     *            the sub IFD. The type of must be one of {@link
     *            PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
     *            PelIfd::INTEROPERABILITY}.
     */
    public function addSubIfd(PelIfd $sub)
    {
        $this->sub[$sub->type] = $sub;
    }

    /**
     * Return a sub IFD.
     *
     * @param int $type
     *            the type of the sub IFD. This must be one of {@link
     *            PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
     *            PelIfd::INTEROPERABILITY}.
     *
     * @return PelIfd the IFD associated with the type, or null if that
     *         sub IFD does not exist.
     */
    public function getSubIfd($type)
    {
        if (isset($this->sub[$type])) {
            return $this->sub[$type];
        } else {
            return null;
        }
    }

    /**
     * Get all sub IFDs.
     *
     * @return array an associative array with (IFD-type, {@link
     *         PelIfd}) pairs.
     */
    public function getSubIfds()
    {
        return $this->sub;
    }

    /**
     * Turn this directory into bytes.
     *
     * This directory will be turned into a byte string, with the
     * specified byte order. The offsets will be calculated from the
     * offset given.
     *
     * @param int $offset
     *            the offset of the first byte of this directory.
     *
     * @param boolean $order
     *            the byte order that should be used when
     *            turning integers into bytes. This should be one of {@link
     *            PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
     */
    public function getBytes($offset, $order)
    {
        $bytes = '';
        $extra_bytes = '';

        Pel::debug('Bytes from IDF will start at offset %d within Exif data', $offset);

        $n = count($this->entries) + count($this->sub);
        if ($this->thumb_data !== null) {
            /*
             * We need two extra entries for the thumbnail offset and
             * length.
             */
            $n += 2;
        }

        $bytes .= PelConvert::shortToBytes($n, $order);

        /*
         * Initialize offset of extra data. This included the bytes
         * preceding this IFD, the bytes needed for the count of entries,
         * the entries themselves (and sub entries), the extra data in the
         * entries, and the IFD link.
         */
        $end = $offset + 2 + 12 * $n + 4;

        foreach ($this->entries as $tag => $entry) {
            /* Each entry is 12 bytes long. */
            $bytes .= PelConvert::shortToBytes($entry->getTag(), $order);
            $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order);
            $bytes .= PelConvert::longToBytes($entry->getComponents(), $order);

            /*
             * Size? If bigger than 4 bytes, the actual data is not in
             * the entry but somewhere else.
             */
            $data = $entry->getBytes($order);
            $s = strlen($data);
            if ($s > 4) {
                Pel::debug('Data size %d too big, storing at offset %d instead.', $s, $end);
                $bytes .= PelConvert::longToBytes($end, $order);
                $extra_bytes .= $data;
                $end += $s;
            } else {
                Pel::debug('Data size %d fits.', $s);
                /*
                 * Copy data directly, pad with NULL bytes as necessary to
                 * fill out the four bytes available.
                 */
                $bytes .= $data . str_repeat(chr(0), 4 - $s);
            }
        }

        if ($this->thumb_data !== null) {
            Pel::debug('Appending %d bytes of thumbnail data at %d', $this->thumb_data->getSize(), $end);
            // TODO: make PelEntry a class that can be constructed with
            // arguments corresponding to the newt four lines.
            $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, $order);
            $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
            $bytes .= PelConvert::longToBytes(1, $order);
            $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(), $order);

            $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT, $order);
            $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
            $bytes .= PelConvert::longToBytes(1, $order);
            $bytes .= PelConvert::longToBytes($end, $order);

            $extra_bytes .= $this->thumb_data->getBytes();
            $end += $this->thumb_data->getSize();
        }

        /* Find bytes from sub IFDs. */
        $sub_bytes = '';
        foreach ($this->sub as $type => $sub) {
            if ($type == PelIfd::EXIF) {
                $tag = PelTag::EXIF_IFD_POINTER;
            } elseif ($type == PelIfd::GPS) {
                $tag = PelTag::GPS_INFO_IFD_POINTER;
            } elseif ($type == PelIfd::INTEROPERABILITY) {
                $tag = PelTag::INTEROPERABILITY_IFD_POINTER;
            } else {
                // PelConvert::BIG_ENDIAN is the default used by PelConvert
                $tag = PelConvert::BIG_ENDIAN;
            }
            /* Make an aditional entry with the pointer. */
            $bytes .= PelConvert::shortToBytes($tag, $order);
            /* Next the format, which is always unsigned long. */
            $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
            /* There is only one component. */
            $bytes .= PelConvert::longToBytes(1, $order);

            $data = $sub->getBytes($end, $order);
            $s = strlen($data);
            $sub_bytes .= $data;

            $bytes .= PelConvert::longToBytes($end, $order);
            $end += $s;
        }

        /* Make link to next IFD, if any */
        if ($this->isLastIFD()) {
            $link = 0;
        } else {
            $link = $end;
        }

        Pel::debug('Link to next IFD: %d', $link);

        $bytes .= PelConvert::longtoBytes($link, $order);

        $bytes .= $extra_bytes . $sub_bytes;

        if (! $this->isLastIfd()) {
            $bytes .= $this->next->getBytes($end, $order);
        }
        return $bytes;
    }

    /**
     * Turn this directory into text.
     *
     * @return string information about the directory, mainly for
     *         debugging.
     */
    public function __toString()
    {
        $str = Pel::fmt("Dumping IFD %s with %d entries...\n", $this->getName(), count($this->entries));

        foreach ($this->entries as $entry) {
            $str .= $entry->__toString();
        }
        $str .= Pel::fmt("Dumping %d sub IFDs...\n", count($this->sub));

        foreach ($this->sub as $type => $ifd) {
            $str .= $ifd->__toString();
        }
        if ($this->next !== null) {
            $str .= $this->next->__toString();
        }
        return $str;
    }
}