diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php index 8377f1f..e41bd3e 100644 --- a/core/lib/Drupal/Core/Image/Image.php +++ b/core/lib/Drupal/Core/Image/Image.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Image; +use Drupal\Core\ImageToolkit\ImageToolkitExifInterface; use Drupal\Core\ImageToolkit\ImageToolkitInterface; /** @@ -17,7 +18,7 @@ * * @ingroup image */ -class Image implements ImageInterface { +class Image implements ImageInterface, ImageExifInterface { /** * Path of the image file. @@ -106,6 +107,26 @@ public function getSource() { /** * {@inheritdoc} */ + public function getExif($tag = NULL) { + if ($this->getToolkit() instanceof ImageToolkitExifInterface) { + return $this->getToolkit()->getExif($tag); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getExifStatus() { + if ($this->getToolkit() instanceof ImageToolkitExifInterface) { + return $this->getToolkit()->getExifStatus(); + } + return NULL; + } + + /** + * {@inheritdoc} + */ public function getToolkitId() { return $this->getToolkit()->getPluginId(); } diff --git a/core/lib/Drupal/Core/Image/ImageExifInterface.php b/core/lib/Drupal/Core/Image/ImageExifInterface.php new file mode 100644 index 0000000..c021184 --- /dev/null +++ b/core/lib/Drupal/Core/Image/ImageExifInterface.php @@ -0,0 +1,56 @@ +getExifStatus() === static::EXIF_NOT_PARSED) { + $this->parseExifData(); + } + + if ($this->getExifStatus() === static::EXIF_PARSED_OK) { + if ($tag) { + return isset($this->exifData[$tag]) ? $this->exifData[$tag] : NULL; + } + return $this->exifData; + } + + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getExifStatus() { + return $this->exifStatus; + } + + /** + * {@inheritdoc} + */ + public function setExifData(array $exif_data, $exif_status = NULL) { + $this->exifData = $exif_data; + if ($exif_status !== NULL) { + $this->exifStatus = $exif_status; + } + return $this; + } + + /** + * Parses the image file EXIF data using the PHP read_exif_data() function. + * + * @return $this + */ + protected function parseExifData() { + if (!$this->isValid()) { + return $this; + } + // Check if EXIF is supported by the image format. + $mime_type = $this->getMimeType(); + if (!in_array($mime_type, ['image/jpeg', 'image/tiff'])) { + // Not an EXIF enabled image. + $this->setExifData([], static::EXIF_UNSUPPORTED_FORMAT); + return $this; + } + // Check if file path is available. + if (empty($this->getSource())) { + // Most likely a new image from scratch. + $this->setExifData([], static::EXIF_RESET); + return $this; + } + // Check if function is available. + if (!function_exists('exif_read_data')) { + // No PHP EXIF extension enabled, return. + $this->logger->error('The PHP EXIF extension is not installed. The image toolkit is unable to retrieve image EXIF data.'); + $this->setExifData([], static::EXIF_NO_PHP_EXTENSION); + return $this; + } + // Read data. + if ($exif_data = @exif_read_data($this->getSource())) { + $this->setExifData($exif_data, static::EXIF_PARSED_OK); + return $this; + } + else { + // Invalid data. + $this->setExifData([], static::EXIF_PARSE_FAILURE); + return $this; + } + } + + /** + * {@inheritdoc} + */ public function getRequirements() { return array(); } diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitExifInterface.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitExifInterface.php new file mode 100644 index 0000000..79c8936 --- /dev/null +++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitExifInterface.php @@ -0,0 +1,101 @@ +29T^kw&^fBn70Al17kD>9a4v`}R5K zIq&wp9{Yfp zCgd(Yav0D}3_Jh}3RZS@Hkg>ZF$94&u=h9x#89z!Hc;TWpkwcRmO=D!NWY_~1u=Bo zoxT;)_Fmr&;s^KoArQmI-Sztb><{rCqk|af-ewZC@rr{7y1}F`fH(-m2;d~-J_L=j z?&D-*WrfPR^8=Y5=zHu3+QZ!AMX=&Q)?MC9z?`FI-^I2JVzhg_1!DB&JvoTUK|Knn&jzswh|%wD06+@*G3XN# zLoYmd72Nkr3(h7!#0&V#3St6K5Ag|M!g~xtfcP&=0%FL@U_}G~(m(#eYnlwyOMrSM zP)`BkuOOZQF(qgNE&#v-Tq!i59t;b34Psib7l@w-5P$vS2P`xFg~8%~py$6mL2?Ws zSm5>+>bK^u46Y{7KNJ8b0^VDEZ*S3Qj-gBdKVaZb^=}Con+U21Dx`3G``gm@4ue+^ z!1Le?Xn+)d(^KF%kOl1C`u(2kI~~J)-5&u3sDmzj0P}mr1sKEsghXWIrTG4bI|sR} zl7!S<3rR~y8&@MoQ*sScM<+`=TXHsLR<{3%v%l4l9-F`n^}}Qt9CHHUa+%6kefi&Am{@H#Iqn)0r?WNg)k%@2>+EXu=F2k5}e34 z7kBgf?^*tjHviQU7H9|wv7E5#403%Ug1!4eFM?N|34jS+&HlHqZ?kS85*+g_CTPh7 z{s7MbfD_C#gx(UoCHPYh09l~jTLMUZ`+fbtbNbu-pAUfieukmJeE(<3RxYe%Jn6^AGyJodL+55JGpmMF^6xqb}G#5lNlpFklZA|fLpV2EVj%PXsE>l;5dw+;@Ej!#a{ew|-nfFVP}!otA9Lqdjv zb_E#*1NOlqHaJXCWq2b8EDCmi1Z=U`_mv%plpHGiI4>MWkZ`Fum#7aQQU4D5zZUcB z|E-|^74yGBzMTP(VZfaj1`Gxu419Z6saG{CUugJU=5)3TL$PikOh3o=tJ~RneL_j9 zlx&#o@QbY}%l>uZKB8%~&}q6_VB~{?a%8eEg+J z1aq;Y`V{?VrPu9;ErM)H?OdU0AGTCkHy)^?y*ws&DbZKU>mSwCPTsC=v>U`3-P4YB zOi6p=$AiJ_t9(-Ma7XERy3y;*J>7CL12$Q88&BH4p1kfNLzhu+IXdQYW`P!KUt6ZH zi{t$%Z8J}$bQxk}#cZ6s@1%pZoo`B^ zQJ!E$m{1h-9l;7XAB$Vja7CIarWL&DPa{*P&dtH`KAxUXiFd&poRH}`ZF26o1tb)N zb-Zg%b5Pa_y=C%cXb^gzOTcy^sK$!v#R!cyt{%hwVo|SP^zqdPx@Ef3`S5f) zq2$K#CfrJ6Vt=8p@5!iT7Fp`JXl5Rnpdg3c0_HaD#W07YXsd`h0B*cWfU4MqPr;;uBW)6B34`JPQSDH%WcZ zk6L31#`CA7BcjAPU!bVZE9-&?1g?EAPMBKer2Pm?@NHqD6yOz)HWgBe>8PnqzBe&9 zVlW$|p0L@Km`pUAJ3%G%;GakMnSY3I_%$WWfKc{0$Q_4}o7QogU9?98#iJ00i_Byl>}js{>F^iWcCR(obnf}T36Byx zAq&(G?JSe#_v;pvFyglUymP8>VQ}T1*w8x4D36XVz2#W;4z9S_h^6|<{6+}x77%^> zu^@Z*qRO1bWj>ytAxfl-)aj${I6FIX_$`nX)|uVHJ!Yh=S(sczAEU_-A5~-4k-AfC z>msopaM`DXaZIqMS8dVnjBRYPfn^*C=h^!9&t9`dX)V{-Uxg?Lf;?wKgyhd||F0qv6mfrH}<{^FM?>D-Sgife5 zJ_@AD0M0{8!<<{DHP7Q^WL$Kn8@2OEb1duROVbkLS>6)B#`-^&oMU|!5%5}S+^71N zv39PIuBS~etm(C2GVjHRDob48jJ)nb*gQcGnXJ>Qrozp3BRhsWm8e=J3{-_0`ytd1 z4`&=s?)Upd#=h+m?p!1~c9PfgnNDAoB9aRSq%P&h&z^EVqzJ_4MG*7vIP11fIbfBT zki^CuVb(e^jxawiyTHnvG$nYP!$X569#$1rV9{zT!+HHIGP4VtP^1Wyv;d zA-(||Q<4T>Xb6?!2WXFFDUWVYU2aUGu*NAwk zv65?vm3|(l#cW%F!+U;?!Xfrjd7A98sHsuKlDaZWX=v{hJc{rIKVHycpWBo+$8)|s zNVxntCA3^$$%Ewl;vA3hf~5Xse<@v_^3kVqC$X79$rQDoR=N;;9x9lxDT^o8*0CS- zsPh1f>vr!vp>HxUzvg# z2=k!trN_~QTFUI6H_AMgRJ{BVY#m0E5W-UxMS?49EycCMmvw^Jgd2rB4BJT?&U`jw zYc5k}*5W0CrL|ujr>P>&dSv^X>bX63Q&s~m*UwvgB=cAEe5v0D)6m=5ymWS@T*K+@ zX6^~$dR()y9!|OcZz>RS{@#i*##Gw^&eVbOm~+R`Z%Pa3lCWhY{^*~I}}wc zw;v%H+)2&9QFyBr*p;o$JiUcY zn^%`foYvp$7XxQWp6P04vr`f{g&Z;Pf=B_s z#u!bptS&5u=_mtk0WZQq2AY}@8=bjEy15;DC>&e<74x=H{f4lfPffavqu8m+;oKf&~i?UrIbL#zTNhjiPL0*G2#(@IJVOfzJx(_s>Z?REJSGhYh~48vu4tRuhc(JHd-DrZbk!^Q;Vq%7K~I>Wua}*g zlRB<`4%K+V)lC!x!|vE3C^e9LVE?e|OfTAMcA}Ucd%Kdp(qii+ng%q&w%QoQD_M$8ihi{HnovSJ3!6G6R3 zTNpkk%X>QJ6EYgFb7qk_IM`$oxpt_yD6ct!FC0=T>5j>J^(6D4BxS5M>dR)T4cCX7 zvS^7$Vx*N7B`<9^6RM~A92wtcyRDO^!kU+z{ zboYE>X@eD9c*vxPva2Mk+q64bGIT?WL8tY+QGo;;9i^1b^dnsuO8A33Iw3=s!BbO- zGV2!fN^J>j|NKNru>eA3K+$(?CjD^{N;S-Yet5OGL1yxY^;^bq5?l=9?FT11H@c5m)%!sDF0voYBt-eEAalFB4TjFo~IICCk65(() zNm8Bn8A|c+MCjE6@2Ac#=1Iqz+m2l)XI!J&Z5PNG<*?)eq#IY6quC1GIURX1>^-k) zzEp=FQ(IHXI~ULVI6UXZ>lftXi0uvVM$W23W7hzF1~Smd}ddgstuP^ojoGI(~)r*gEi?HQ_+x=C#Z@6NRi^bR1 znJIMb%=5{KaULDxw#g@3r)uK^0-Vt0U%2`NdNwLiMC&)Z{2nglXeh#`h4u3)3E!!R zS~t{9FKg2q;wI7*(Cd_c#p;u$KN@Xj^W>E98hC@G-~lZSbJ%xPyrppyoG@A`rLcb$ z_f4iypHA+Tin1b{1SQ61b#5Ejs_goSZ;bY>nhsR$tP#$r@*p+NlO4 zVb>S@w?J@A&%*(8SB_|wFHcQWq=yAoU%_gSd=I7`Ti3FsW|RI#&eKlr88JdPn`2$f zS)iD@BO!Ys5l;Fx#zgPQL4+UCny-P=pw;H-dx1I>gvbPw59W3XG8ctcgJ$b9HjfvR zvv_!#XR6nWMYOJMf@iu#a^SjoeUuq*_7$fCIWLhtbx(^0vqR3f;t)9aqFY( z^311ZA2a)|~G?@;-WW2|XD`!WYSq9Pe;AVx-Bb-rTg?T~0Z{@z3C+@Cr@ zsVCLJ9Gy6~gcLl?kSW49CF<*JUNEzHA-P2VnZD0uiz6UI$zz2^IJm_-C^)=mbnCRp zAzPY~F&c(}ukwcUjSm4?y5o;*nb6Z=g~UWlgZxyjAMeTLU8YoI*qQ?cyqeWo@p%uZ zD=1^)oPIGqtMxv2X1>{rPYTXoctsZJ=s?*P%7uZ6xnxYgxMJCH%@DRDF{?RUefBRZ43~JQRljzn`gAH2y_cv32PdZdJ1O+(ulPE6mBDctU)W&*2Xb`%hmJ z`6cKi<(9YbgdUA&khfXd`#py}7a>h^DO_vxmKEU=(e|II%J|?h8JjM#r$7ISA|ErF zDM5Y2Kq{l76PKPNk={*cIhNUk?xT+?KD}pHu&2RUo62>B-m57w8%5pt?Q8^N^j-O~ zey4g5G}!{);T))4&`&L>Mm;sBZQO9vD-f_~pV9v;3VKDDi6Gzx3XWLxP%dlV5UGaYWQ{(yW z8iGWMRSos~;W~WlN85`pDTySvBQ6(xUNl(TYzj_i4&+HVn`P=+4OR^D)O`CPo%2TU zp-EWgCu34tf6u^#%qy4oP4g!XoN?7&39*}+X@OVPDIe=(n&k@j^!zFhGL7|I6i}~& z(sRS7PAiG*w_{;QU4md>UCAQN<*aEV=5z)*aidt(@mq%-ImUjtm8{_R%c3caZoi_h zDj>CX`YibT;vL^w@~IvjYjfBV6_5}25cSv3v5QF+MXK||@s+6qn2WGc9#4K7n~{0q zPE@=)<`kj}vrt#3mwKOS*_$$*i~&=_<4E79L`PDDnmD6BRi-%m|_*V+sFD8bB z2KkG1Upbbjq?KiIotpBz72wCU$eU5aaY!@C5IJ2=nN;NeNagTGQoMiK?(oR);^I zXRWvPS=i~LInT1%$`_Plt5lgIlgaH`+unBw?W8rweGEARu9cffBD!V0hDp5NGXk=D z7KKY4`My|^R=TdHWhMHNUv6L@(D!kbtr?Nb>e85~^w9aAsoesM6Paijw*aD-oABY_ zudCwpQZl#CbDOEP>D1K5qjsp!H(cF=u3Y7zb{UxN(r0Kx_4zaTGb6{QV={w=w)v4Y z$Ly6Uhei#2bg(uT6H=4Rs}Wo&pUo|uXa*eY-AQv5Vh}r`Z;*1EgEPVi+bnuU(z$wd@W}W_P3YrxaPe)=_qYLxmnv z((E_H#4Tw#EDkkJwDjV_dqxPu>0MdF0uC3iis5bnsO2a;%Ivtd#|SH#JtyV3t(eLw z)v1x?a953_gTzm*M1LggS z=9{DS#iB1B4Vb0It;&6wVLz3xt5SSd882I#6e+NX#%NmMH1^ZLsn9^))01&Xl({R4 z7p9`PZwYT$`N@)}*t%PTAZ6adnvx!AP_>spyj-~s{?x8?NmtqfC3l+ra%Wq6!DvYl z^xhYuF@d35(1AR|i+rBf){(IpN_z~rt}nhQI>})*N|#WK^y|jN9y#BjqbmbcT^%RlGaB@@{ z^*)ga+6WX*Mxq!3b8cQ<0c&- zOiS?Dy~v?LUV%Tqp=dw}L1SFmFQHY^a&wftsZ_&=~-ujuy&VztVpJq)#`;~!5 z_KLyD%E5;X`8%uP&F*QZx4^Q4hL@1cfgXH>JImO9VNE=^VC64c ztE38Kv!(kpPI^i(Z-GvEuMu=o&gk+LJrxC|I2}33@Nij~md2#K42=xBJb&5Qy1{nJ z?}x-UI84WJR>^y)vB)w4vZd534f?sqdZ=0}lf{V2`ExB(#R;BST^&5RHHN0AW?pv7 zyt~Xyj4Vt_-3o_41Da^4h)J(*xRPo7xtfYc&+wic4wEx#=e^LyU!n56Nt~Ob8yb=4 zuIx?Nz@AlX>n*&&oLr1e-hMfYfUNEhoNRlesML?`yDx13=i_IXgHz+#fb~pCd^Aqs^W2`@y#p8_R>Mu}zYa|zl=os8ud(48^BQ>A3(u=g zr4bV6DaDON(Pcsm&jXJ$Mf{XBGLONFhPa{z>&*CZ({$$nTm=_ipt0jqx!2R}#yujQ z4q8z|9x1D73Ar;Pf>FODI+0j3LWUHL^wEM$zvXSw7YEZf@2n8?7#di)ZloY`7S z5PQNxGJJk*^`>kNW{Rw6bqeQk9?b-|>kn5ojWM}gN&V(IwyM^{L^AF(1OfVKqwDvtV9a@D z_-?SnmvGe8FP*=s^TIgp`_gBYwyj0woEk2e#QrkrVoeF$x&<1yg!3d=-aL9IlC*o! zzWL6>C*h`n>w}W~Nd+I>kGT{EGH5Ndb4#aNz*qQUJh*+5Y@z-ZXv@5|@--~J{$8J1 zEdIr4Uog1j7C1Z1#0z|0D;6{07^WbNnTDnKb3*4**uJ&=!FLfml+S%fWqiihmW8RG zo~)+z@eY>+B{-<+P~-Sgq*Kk$#(VER+8-wvI&a%s(;uzo_vD|bSEZExpl17W-&!Ix zt=dIX=ZAQH-Nf|mw?F4#|A*x7^FMI_0S*ox?g0WkJUk*I0(g#xg8c9yG65zQ8a4?b z87T=NF)=w6GYvTfBPB60EjJw_D;p;#Cm9W|AP>6$GY2O-c!((e`wZ|QGCm3lKKmo$ zN9_O0baw_Q2blbQ1_=4qH8kX$@81J{0DfHu78(lvwhIuwJLZFW00#{R2Yzi7Jm~w^ zF&`Wjg{U&Nkpp@h@dy@^4Rrm0-5(2kU(Beo!y)$le>?~qnSn_qfA`41 zBC)m-_VpsEgeq>NS$ns}^IM=|d07$f^aJ@aeWI@DnoWS{SROCMj<}mW^ICt!mN4E? zw6`X=`A&?RX$w37Pg8s$kDag>+GH9kx>BQTMGybL;AGBxFs-(^afU9QZkRg01gJa1XZ@lQ)=G(P=MljCZXTiQjKzBTY2_CVna zQjDUaK?3?0z3beW)!3TSiln^Bn%)7lQBD4Y#j;aaGVzo%g{hj<)Df)j^L&f(q0*NV z;{tR)X}4YRRG*CadS;69JC`yhL%Dj|9N-UM$~mpcdVVORe+T2MtjjqYo)uqE{j7tk zeUG@36h~08I}b6BKalY0rJC>vrg7P?TcDK|R&cf8C57a3ry8fvqCAg_^nng)!m&5; zaADRN%VM(x)GqUxlo9@$KlQAW&-kp)EQ<=f6bJ=pLske@O&s=$AB{a*1b9{3lpnJ1 zVy-&s2{cWb3=k&|FVrFQ4w%yj)EWBUcu$ONI>w2AJZ$z>d zoACvP4m;=^o;?l2&oNInrdE`>w|XA8bHo(cM~>_@QyYx(=$mO%zVm@gj8{`S0Zq3QOT>RKYyzO#dW2 z+!$=r!qfZoIim(f0Movbh;D?hk(G)}KQw!YODQM_;N?w~^j9)O zFF$sHO|%l7e5P@iqQWPc#MZsoQ4HdFcoh|=r){mnSG zMFvTyYc_4_&@%KnD$j4<9U{`~MBK<;j_SuizjAw0KelnIJNt=3Ad;d`>tJO%D{9C8 z%n^;^E#stCN$GOzM+$mq(P5OY8*}aI@`990RF~2HC^O9^wW^d=#qcUQX~%*>X2F~9 z_M~;qkOlhs;J^5irBghYtBl^^4K8399vPK#qG05G_RC}ejl{#_+^bkes#ch=H#aSi zq1qV#2feF3d+!F-g|i-5)aU=A%P8T^%U5S!v|lrylySsI2@y|aU@bbpAO=)! zlSwBwth@Z&S60b)W#~!JtPvrhu>T(1kr}k4HfJzn*`Iy5rpzkKtunpE$`>nFoE1V? z{ZMb`@w-Bcz3ttb(Xy5qb|Zgye*fcc(O(mMhSvE(Lc5j(31wgLH(w5Z~El*)zv{z076PbQ<@N zROC|Cs@mElB23j`P7W(#CBE;>+ft_!nZ7>}wdsvKixQX%R~=#C4*2qLcNVcI22$JN%X zXW6JL7`WO>ju@qte==rn%sXO2IkW`6a+Qs|Sq`CSDJB?Y76taxu%c(MpLh}x*iyy@ zR7K>F%XbhJGWGCK7Es1mnoRIF8bdh;X5TOmTWJ(C7m{Pq1b8t8@f^>NdwINif9m0D z?Ad_vNjN+?J=5j8i=c2I*%S=!+j-(Lq5!4hYZ`H)%x6j3srGQhF=8um51rQw>*Q#| zR`}gl4?jg07|I^Jb$@oreW0eSn>r1bVG`ROf=qO-_W)rNm?ame6+5tdtWqA zX}F6!d@Vj+xO#>y{zkV(+$GT-?HS8ipsKTN?ICri9L1Lg<&QwsxgT3J<5iL3CsFx` z)vSyDFSUn#u7U~f`W-2Z1VN6hpNckn+D4OYkf$QD@k2Ct=IntSvvM9U2AO^ztlV$^rlZ?3}-M;du5_4>% zbTv?j#}D5EbS?{Qf;-KUw9^8Gx$fxF%QeT(3h{BDZj_Yg{KVboHV-p(f(@UK5$Kz( z_CWSg%SC1Tf!KS2ldZZnPy0#T=9NKv=?_PKm(UQPGH3X{;~(SBx@z<}_XLl%{nR9TE2^}?r?OJ7exP!RPU>-&FY&U5|Yq% z+uGf4KHUNoibHu)H1#>el-@<*&0ouO^RdmB>XnL2>h|8BCV$LeFhdD4?NjX$^J*N` zZ-RZ?qm5KBUcg}OY+bCa%k*iPCoR-7LC7Wvd{@zqO{27Z(AJ!?`?JOcOYh9Hs*AS$ zIyPvz8VZGcQq5D&AMXvtrA$%>#!ykgoN47nXaHHdCgA{Pp}yptc^12B>uO!4+~spg zOWW$EcY6H#x^AqFqsE&CRyxx`oz@7tt;|ixA-PdmCrA0$V|t?ubTE?Z0_gUMR5C;v zJJBcRSG2+Av)`QY635IQ*_ZT7PErEV-jwzv6I>Y>1RDl>6#B#aR2d9-Zl4>|Foxj@ zB2ub~9c;eRtyDH8i`%rnwxtq?Fi%Mc75wbbO!M&VsIDbtEY3&}ftCq|+{&{GjpgoX zU$wR+mHrK+?38vi`iaVqBuwOF6Vdy{5ArHD?a+Ub7Y(F&=6~HU)pe7Sj`4k}`GWQq zaPUbWPn_Vdz8=wI2fvAvtj-v|sn!T0vE}z8-t_qr$N5}{y5K+HsL`t%zX|BS{r2Ag D@^VkR literal 0 HcmV?d00001 diff --git a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php index d582c68..2298a04 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php +++ b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php @@ -8,6 +8,7 @@ namespace Drupal\system\Plugin\ImageToolkit\Operation\gd; use Drupal\Component\Utility\Color; +use Drupal\Core\ImageToolkit\ImageToolkitExifInterface; /** * Defines GD2 create_new image operation. @@ -128,9 +129,13 @@ protected function execute(array $arguments) { $this->getToolkit()->setType($type); $this->getToolkit()->setResource($res); - // Destroy the original resource if it is not needed by other operations. - if (!$arguments['is_temp'] && is_resource($original_res)) { - imagedestroy($original_res); + if (!$arguments['is_temp']) { + // Destroy the original resource if it is not needed by other operations. + if (is_resource($original_res)) { + imagedestroy($original_res); + } + // Reset EXIF information on new image. + $this->getToolkit()->setExifData([], ImageToolkitExifInterface::EXIF_RESET); } return TRUE; diff --git a/core/modules/system/system.install b/core/modules/system/system.install index afa5892..d0200c6 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -195,6 +195,7 @@ function system_requirements($phase) { $required_extensions = array( 'date', 'dom', + 'exif', 'filter', 'gd', 'hash', diff --git a/core/tests/Drupal/Tests/Core/Image/ImageTest.php b/core/tests/Drupal/Tests/Core/Image/ImageTest.php index d39d8ca..f4a2aa6 100644 --- a/core/tests/Drupal/Tests/Core/Image/ImageTest.php +++ b/core/tests/Drupal/Tests/Core/Image/ImageTest.php @@ -8,12 +8,14 @@ namespace Drupal\Tests\Core\Image; use Drupal\Core\Image\Image; +use Drupal\Core\ImageToolkit\ImageToolkitExifInterface; use Drupal\Core\ImageToolkit\ImageToolkitInterface; use Drupal\Tests\UnitTestCase; /** * Tests the image class. * + * @coversDefaultClass \Drupal\Core\Image\Image * @group Image */ class ImageTest extends UnitTestCase { @@ -78,14 +80,16 @@ protected function getToolkitMock(array $stubs = array()) { * The name of the GD toolkit operation class to be mocked. * @param ImageToolkitInterface $toolkit * The image toolkit object. + * @param array $stubs + * (optional) Array containing toolkit methods to be replaced with stubs. * * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getToolkitOperationMock($class_name, ImageToolkitInterface $toolkit) { + protected function getToolkitOperationMock($class_name, ImageToolkitInterface $toolkit, array $stubs = array('execute')) { $mock_builder = $this->getMockBuilder('Drupal\system\Plugin\ImageToolkit\Operation\gd\\' . $class_name); $logger = $this->getMock('Psr\Log\LoggerInterface'); return $mock_builder - ->setMethods(array('execute')) + ->setMethods($stubs) ->setConstructorArgs(array(array(), '', array(), $toolkit, $logger)) ->getMock(); } @@ -98,11 +102,14 @@ protected function getToolkitOperationMock($class_name, ImageToolkitInterface $t * to TRUE. * @param array $stubs * (optional) Array containing toolkit methods to be replaced with stubs. + * @param string $source + * (optional) The path to the source image file. If NULL, the file in + * $this->source will be loaded. Defaults to NULL. * * @return \Drupal\Core\Image\Image * An image object. */ - protected function getTestImage($load_expected = TRUE, array $stubs = array()) { + protected function getTestImage($load_expected = TRUE, array $stubs = array(), $source = NULL) { if (!$load_expected && !in_array('load', $stubs)) { $stubs = array_merge(array('load'), $stubs); } @@ -118,7 +125,11 @@ protected function getTestImage($load_expected = TRUE, array $stubs = array()) { ->method('load'); } - $this->image = new Image($this->toolkit, $this->source); + if (!$source) { + $source = $this->source; + } + + $this->image = new Image($this->toolkit, $source); } /** @@ -126,13 +137,18 @@ protected function getTestImage($load_expected = TRUE, array $stubs = array()) { * * @param string $class_name * The name of the GD toolkit operation class to be mocked. + * @param array $stubs + * (optional) Array containing toolkit methods to be replaced with stubs. + * @param string $source + * (optional) The path to the source image file. If NULL, the file in + * $this->source will be loaded. Defaults to NULL. * * @return \Drupal\Core\Image\Image * An image object. */ - protected function getTestImageForOperation($class_name) { + protected function getTestImageForOperation($class_name, array $stubs = array('execute'), $source = NULL) { $this->toolkit = $this->getToolkitMock(array('getToolkitOperation')); - $this->toolkitOperation = $this->getToolkitOperationMock($class_name, $this->toolkit); + $this->toolkitOperation = $this->getToolkitOperationMock($class_name, $this->toolkit, $stubs); $this->toolkit->expects($this->any()) ->method('getPluginId') @@ -142,7 +158,11 @@ protected function getTestImageForOperation($class_name) { ->method('getToolkitOperation') ->will($this->returnValue($this->toolkitOperation)); - $this->image = new Image($this->toolkit, $this->source); + if (!$source) { + $source = $this->source; + } + + $this->image = new Image($this->toolkit, $source); } /** @@ -265,6 +285,52 @@ public function testParseFileFails() { } /** + * @covers ::getExif + * @covers ::getExifStatus + */ + public function testExif() { + // Test a PNG image (not EXIF enabled). + $this->getTestImage(); + $this->assertEquals(ImageToolkitExifInterface::EXIF_NOT_PARSED, $this->image->getExifStatus()); + $this->assertNull($this->image->getExif('Orientation')); + $this->assertEquals(ImageToolkitExifInterface::EXIF_UNSUPPORTED_FORMAT, $this->image->getExifStatus()); + $this->assertNull($this->image->getExif('FileName')); + $this->assertNull($this->image->getExif()); + + // Test a JPEG image without EXIF data. + $source = __DIR__ . '/../../../../../modules/simpletest/files/image-test.jpg'; + $this->getTestImage(TRUE, [], $source); + $this->assertEquals(ImageToolkitExifInterface::EXIF_NOT_PARSED, $this->image->getExifStatus()); + $this->assertNull($this->image->getExif('Orientation')); + $this->assertEquals(ImageToolkitExifInterface::EXIF_PARSED_OK, $this->image->getExifStatus()); + $this->assertEquals('image-test.jpg', $this->image->getExif('FileName')); + + // Test a JPEG image with EXIF data. + $source = __DIR__ . '/../../../../../modules/simpletest/files/image-test-exif.jpeg'; + $this->getTestImageForOperation('CreateNew', [], $source); + $this->assertEquals(ImageToolkitExifInterface::EXIF_NOT_PARSED, $this->image->getExifStatus()); + $this->assertEquals(8, $this->image->getExif('Orientation')); + $this->assertEquals(ImageToolkitExifInterface::EXIF_PARSED_OK, $this->image->getExifStatus()); + $this->assertEquals('106/32', $this->image->getExif('ShutterSpeedValue')); + $expected_thumbnail = [ + 'Compression' => 6, + 'XResolution' => '180/1', + 'YResolution' => '180/1', + 'ResolutionUnit' => 2, + 'JPEGInterchangeFormat' => 5108, + 'JPEGInterchangeFormatLength' => 7089, + ]; + $exif_data = $this->image->getExif(); + $this->assertEquals($expected_thumbnail, $exif_data['THUMBNAIL']); + $this->assertEquals($expected_thumbnail, $this->image->getExif('THUMBNAIL')); + + // Test resetting EXIF data via createNew. + $this->image->createNew(200, 200); + $this->assertEquals(ImageToolkitExifInterface::EXIF_RESET, $this->image->getExifStatus()); + $this->assertNull($this->image->getExif()); + } + + /** * Tests \Drupal\Core\Image\Image::scale(). */ public function testScaleWidth() {