ExifTool.pm 316 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636
  1. #------------------------------------------------------------------------------
  2. # File: ExifTool.pm
  3. #
  4. # Description: Read and write meta information
  5. #
  6. # URL: http://owl.phy.queensu.ca/~phil/exiftool/
  7. #
  8. # Revisions: Nov. 12/2003 - P. Harvey Created
  9. # (See html/history.html for revision history)
  10. #
  11. # Legal: Copyright (c) 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  12. # This library is free software; you can redistribute it and/or
  13. # modify it under the same terms as Perl itself.
  14. #------------------------------------------------------------------------------
  15. package Image::ExifTool;
  16. use strict;
  17. require 5.004; # require 5.004 for UNIVERSAL::isa (otherwise 5.002 would do)
  18. require Exporter;
  19. use File::RandomAccess;
  20. use vars qw($VERSION $RELEASE @ISA @EXPORT_OK %EXPORT_TAGS $AUTOLOAD @fileTypes
  21. %allTables @tableOrder $exifAPP1hdr $xmpAPP1hdr $xmpExtAPP1hdr
  22. $psAPP13hdr $psAPP13old @loadAllTables %UserDefined $evalWarning
  23. %noWriteFile %magicNumber @langs $defaultLang %langName %charsetName
  24. %mimeType $swapBytes $swapWords $currentByteOrder %unpackStd
  25. %jpegMarker %specialTags);
  26. $VERSION = '10.10';
  27. $RELEASE = '';
  28. @ISA = qw(Exporter);
  29. %EXPORT_TAGS = (
  30. # all public non-object-oriented functions:
  31. Public => [qw(
  32. ImageInfo GetTagName GetShortcuts GetAllTags GetWritableTags
  33. GetAllGroups GetDeleteGroups GetFileType CanWrite CanCreate
  34. AddUserDefinedTags
  35. )],
  36. # exports not part of the public API, but used by ExifTool modules:
  37. DataAccess => [qw(
  38. ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u
  39. Get16s Get32u Get32s Get64u GetFloat GetDouble GetFixed32s Write
  40. WriteValue Tell Set8u Set8s Set16u Set32u
  41. )],
  42. Utils => [qw(GetTagTable TagTableKeys GetTagInfoList AddTagToTable HexDump)],
  43. Vars => [qw(%allTables @tableOrder @fileTypes)],
  44. );
  45. # set all of our EXPORT_TAGS in EXPORT_OK
  46. Exporter::export_ok_tags(keys %EXPORT_TAGS);
  47. # test for problems that can arise if encoding.pm is used
  48. { my $t = "\xff"; die "Incompatible encoding!\n" if ord($t) != 0xff; }
  49. # The following functions defined in Image::ExifTool::Writer are declared
  50. # here so their prototypes will be available. These Writer routines will be
  51. # autoloaded when any of them is called.
  52. sub SetNewValue($;$$%);
  53. sub SetNewValuesFromFile($$;@);
  54. sub GetNewValue($$;$);
  55. sub GetNewValues($$;$);
  56. sub CountNewValues($);
  57. sub SaveNewValues($);
  58. sub RestoreNewValues($);
  59. sub WriteInfo($$;$$);
  60. sub SetFileModifyDate($$;$$$);
  61. sub SetFileName($$;$$);
  62. sub GetAllTags(;$);
  63. sub GetWritableTags(;$);
  64. sub GetAllGroups($);
  65. sub GetNewGroups($);
  66. sub GetDeleteGroups();
  67. sub AddUserDefinedTags($%);
  68. # non-public routines below
  69. sub InsertTagValues($$$;$);
  70. sub IsWritable($);
  71. sub GetNewFileName($$);
  72. sub LoadAllTables();
  73. sub GetNewTagInfoList($;$);
  74. sub GetNewTagInfoHash($@);
  75. sub GetLangInfo($$);
  76. sub Get64s($$);
  77. sub Get64u($$);
  78. sub GetExtended($$);
  79. sub DecodeBits($$;$);
  80. sub EncodeBits($$;$$);
  81. sub HexDump($;$%);
  82. sub DumpTrailer($$);
  83. sub DumpUnknownTrailer($$);
  84. sub VerboseInfo($$$%);
  85. sub VerboseDir($$;$$);
  86. sub VerboseValue($$$;$);
  87. sub VPrint($$@);
  88. sub Rationalize($;$);
  89. sub Write($@);
  90. sub WriteTrailerBuffer($$$);
  91. sub AddNewTrailers($;@);
  92. sub Tell($);
  93. sub WriteValue($$;$$$$);
  94. sub WriteDirectory($$$;$);
  95. sub WriteBinaryData($$$);
  96. sub CheckBinaryData($$$);
  97. sub WriteTIFF($$$);
  98. sub PackUTF8(@);
  99. sub UnpackUTF8($);
  100. sub SetPreferredByteOrder($);
  101. sub CopyBlock($$$);
  102. sub CopyFileAttrs($$);
  103. sub TimeNow(;$);
  104. sub NewGUID();
  105. # other subroutine definitions
  106. sub EncodeFileName($$;$);
  107. sub Open($*$;$);
  108. sub Exists($$);
  109. sub IsDirectory($$);
  110. sub Rename($$$);
  111. sub Unlink($@);
  112. sub SetFileTime($$;$$$);
  113. sub DoEscape($$);
  114. sub ConvertFileSize($);
  115. sub ParseArguments($;@); #(defined in attempt to avoid mod_perl problem)
  116. sub ReadValue($$$$$;$);
  117. # list of main tag tables to load in LoadAllTables() (sub-tables are recursed
  118. # automatically). Note: They will appear in this order in the documentation
  119. # unless tweaked in BuildTagLookup::GetTableOrder().
  120. @loadAllTables = qw(
  121. PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw Lytro MinoltaRaw PanasonicRaw
  122. SigmaRaw JPEG GIMP Jpeg2000 GIF BMP BMP::OS2 PICT PNG MNG DjVu DPX OpenEXR
  123. MIFF PGF PSP PhotoCD Radiance PDF PostScript Photoshop::Header FujiFilm::RAF
  124. FujiFilm::IFD Samsung::Trailer Sony::SRF2 Sony::SR2SubIFD Sony::PMP ITC ID3
  125. Vorbis Ogg APE APE::NewHeader APE::OldHeader Audible MPC MPEG::Audio
  126. MPEG::Video MPEG::Xing M2TS QuickTime QuickTime::ImageFile Matroska MOI MXF
  127. DV Flash Flash::FLV Real::Media Real::Audio Real::Metafile RIFF AIFF ASF
  128. DICOM MIE HTML XMP::SVG Palm Palm::MOBI Palm::EXTH Torrent EXE
  129. EXE::PEVersion EXE::PEString EXE::MachO EXE::PEF EXE::ELF EXE::CHM LNK Font
  130. VCard VCard::VCalendar RSRC Rawzor ZIP ZIP::GZIP ZIP::RAR RTF OOXML iWork
  131. FLIR::AFF FLIR::FPF
  132. );
  133. # alphabetical list of current Lang modules
  134. @langs = qw(cs de en en_ca en_gb es fi fr it ja ko nl pl ru sv tr zh_cn zh_tw);
  135. $defaultLang = 'en'; # default language
  136. # language names
  137. %langName = (
  138. cs => 'Czech (Čeština)',
  139. de => 'German (Deutsch)',
  140. en => 'English',
  141. en_ca => 'Canadian English',
  142. en_gb => 'British English',
  143. es => 'Spanish (Español)',
  144. fi => 'Finnish (Suomi)',
  145. fr => 'French (Français)',
  146. it => 'Italian (Italiano)',
  147. ja => 'Japanese (日本語)',
  148. ko => 'Korean (한국어)',
  149. nl => 'Dutch (Nederlands)',
  150. pl => 'Polish (Polski)',
  151. ru => 'Russian (Русский)',
  152. sv => 'Swedish (Svenska)',
  153. 'tr'=> 'Turkish (Türkçe)',
  154. zh_cn => 'Simplified Chinese (简体中文)',
  155. zh_tw => 'Traditional Chinese (繁體中文)',
  156. );
  157. # recognized file types, in the order we test unknown files
  158. # Notes: 1) There is no need to test for like types separately here
  159. # 2) Put types with weak file signatures at end of list to avoid false matches
  160. @fileTypes = qw(JPEG CRW DR4 TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF PSD XMP
  161. BMP PPM RIFF AIFF ASF MOV MPEG Real SWF PSP FLV OGG FLAC APE MPC
  162. MKV MXF DV PMP IND PGF ICC ITC FLIR FPF LFP HTML VRD RTF XCF DSS
  163. QTIF FPX PICT ZIP GZIP PLIST RAR BZ2 TAR RWZ EXE EXR HDR CHM LNK
  164. WMF AVC DEX DPX RAW Font RSRC M2TS PHP Torrent VCard AA PDB MOI
  165. MP3 DICOM PCD);
  166. # file types that we can write (edit)
  167. my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF RAF RAW PNG MIE PSD XMP PPM
  168. EPS X3F PS PDF ICC VRD DR4 JP2 EXIF AI AIT IND MOV EXV);
  169. my %writeTypes; # lookup for writable file types (hash filled if required)
  170. # file extensions that we can't write for various base types
  171. %noWriteFile = (
  172. TIFF => [ qw(3FR DCR K25 KDC) ],
  173. XMP => [ 'SVG' ],
  174. JP2 => [ 'J2C', 'JPC' ],
  175. );
  176. # file types that we can create from scratch
  177. # - must update CanCreate() documentation if this list is changed!
  178. my %createTypes = map { $_ => 1 } qw(XMP ICC MIE VRD DR4 EXIF EXV);
  179. # file type lookup for all recognized file extensions
  180. # (if extension may be more than one type, the type is a list where
  181. # the writable type should come first if it exists)
  182. my %fileTypeLookup = (
  183. '3FR' => ['TIFF', 'Hasselblad RAW format'],
  184. '3G2' => ['MOV', '3rd Gen. Partnership Project 2 audio/video'],
  185. '3GP' => ['MOV', '3rd Gen. Partnership Project audio/video'],
  186. '3GP2'=> '3G2',
  187. '3GPP'=> '3GP',
  188. AA => ['AA', 'Audible Audiobook'],
  189. AAX => ['MOV', 'Audible Enhanced Audiobook'],
  190. ACR => ['DICOM','American College of Radiology ACR-NEMA'],
  191. ACFM => ['Font', 'Adobe Composite Font Metrics'],
  192. AFM => ['Font', 'Adobe Font Metrics'],
  193. AMFM => ['Font', 'Adobe Multiple Master Font Metrics'],
  194. AI => [['PDF','PS'], 'Adobe Illustrator'],
  195. AIF => 'AIFF',
  196. AIFC => ['AIFF', 'Audio Interchange File Format Compressed'],
  197. AIFF => ['AIFF', 'Audio Interchange File Format'],
  198. AIT => 'AI',
  199. APE => ['APE', "Monkey's Audio format"],
  200. ARW => ['TIFF', 'Sony Alpha RAW format'],
  201. ASF => ['ASF', 'Microsoft Advanced Systems Format'],
  202. AVC => ['AVC', 'Advanced Video Connection'], # (extensions are actually _AU,_AD,_IM,_ID)
  203. AVI => ['RIFF', 'Audio Video Interleaved'],
  204. AZW => 'MOBI', # (see http://wiki.mobileread.com/wiki/AZW)
  205. AZW3 => 'MOBI',
  206. BMP => ['BMP', 'Windows Bitmap'],
  207. BTF => ['BTF', 'Big Tagged Image File Format'], #(unofficial)
  208. BZ2 => ['BZ2', 'BZIP2 archive'],
  209. CHM => ['CHM', 'Microsoft Compiled HTML format'],
  210. CIFF => ['CRW', 'Camera Image File Format'],
  211. COS => ['COS', 'Capture One Settings'],
  212. CR2 => ['TIFF', 'Canon RAW 2 format'],
  213. CRW => ['CRW', 'Canon RAW format'],
  214. CS1 => ['PSD', 'Sinar CaptureShop 1-Shot RAW'],
  215. DC3 => 'DICM',
  216. DCM => 'DICM',
  217. DCP => ['TIFF', 'DNG Camera Profile'],
  218. DCR => ['TIFF', 'Kodak Digital Camera RAW'],
  219. DEX => ['DEX', 'Dalvik Executable format'],
  220. DFONT=> ['Font', 'Macintosh Data fork Font'],
  221. DIB => ['BMP', 'Device Independent Bitmap'],
  222. DIC => 'DICM',
  223. DICM => ['DICOM','Digital Imaging and Communications in Medicine'],
  224. DIVX => ['ASF', 'DivX media format'],
  225. DJV => 'DJVU',
  226. DJVU => ['AIFF', 'DjVu image'],
  227. DLL => ['EXE', 'Windows Dynamic Link Library'],
  228. DNG => ['TIFF', 'Digital Negative'],
  229. DOC => ['FPX', 'Microsoft Word Document'],
  230. DOCM => [['ZIP','FPX'], 'Office Open XML Document Macro-enabled'],
  231. # Note: I have seen a password-protected DOCX file which was FPX-like, so I assume
  232. # that any other MS Office file could be like this too. The only difference is
  233. # that the ZIP and FPX formats are checked first, so if this is wrong, no biggie.
  234. DOCX => [['ZIP','FPX'], 'Office Open XML Document'],
  235. DOT => ['FPX', 'Microsoft Word Template'],
  236. DOTM => [['ZIP','FPX'], 'Office Open XML Document Template Macro-enabled'],
  237. DOTX => [['ZIP','FPX'], 'Office Open XML Document Template'],
  238. DPX => ['DPX', 'Digital Picture Exchange' ],
  239. DR4 => ['DR4', 'Canon VRD version 4 Recipe'],
  240. DS2 => ['DSS', 'Digital Speech Standard 2'],
  241. DSS => ['DSS', 'Digital Speech Standard'],
  242. DV => ['DV', 'Digital Video'],
  243. DVB => ['MOV', 'Digital Video Broadcasting'],
  244. DYLIB=> ['EXE', 'Mach-O Dynamic Link Library'],
  245. EIP => ['ZIP', 'Capture One Enhanced Image Package'],
  246. EPS => ['EPS', 'Encapsulated PostScript Format'],
  247. EPS2 => 'EPS',
  248. EPS3 => 'EPS',
  249. EPSF => 'EPS',
  250. EPUB => ['ZIP', 'Electronic Publication'],
  251. ERF => ['TIFF', 'Epson Raw Format'],
  252. EXE => ['EXE', 'Windows executable file'],
  253. EXR => ['EXR', 'Open EXR'],
  254. EXIF => ['EXIF', 'Exchangable Image File Metadata'],
  255. EXV => ['EXV', 'Exiv2 metadata'],
  256. F4A => ['MOV', 'Adobe Flash Player 9+ Audio'],
  257. F4B => ['MOV', 'Adobe Flash Player 9+ audio Book'],
  258. F4P => ['MOV', 'Adobe Flash Player 9+ Protected'],
  259. F4V => ['MOV', 'Adobe Flash Player 9+ Video'],
  260. FFF => [['TIFF','FLIR'], 'Hasselblad Flexible File Format'],
  261. FLAC => ['FLAC', 'Free Lossless Audio Codec'],
  262. FLA => ['FPX', 'Macromedia/Adobe Flash project'],
  263. FLIR => ['FLIR', 'FLIR File Format'], # (not an actual extension)
  264. FLV => ['FLV', 'Flash Video'],
  265. FPF => ['FPF', 'FLIR Public image Format'],
  266. FPX => ['FPX', 'FlashPix'],
  267. GIF => ['GIF', 'Compuserve Graphics Interchange Format'],
  268. GZ => 'GZIP',
  269. GZIP => ['GZIP', 'GNU ZIP compressed archive'],
  270. HDP => ['TIFF', 'Windows HD Photo'],
  271. HDR => ['HDR', 'Radiance RGBE High Dynamic Range'],
  272. HTM => 'HTML',
  273. HTML => ['HTML', 'HyperText Markup Language'],
  274. ICAL => 'ICS',
  275. ICC => ['ICC', 'International Color Consortium'],
  276. ICM => 'ICC',
  277. ICS => ['VCard','iCalendar Schedule'],
  278. IDML => ['ZIP', 'Adobe InDesign Markup Language'],
  279. IIQ => ['TIFF', 'Phase One Intelligent Image Quality RAW'],
  280. IND => ['IND', 'Adobe InDesign'],
  281. INDD => ['IND', 'Adobe InDesign Document'],
  282. INDT => ['IND', 'Adobe InDesign Template'],
  283. INX => ['XMP', 'Adobe InDesign Interchange'],
  284. ITC => ['ITC', 'iTunes Cover Flow'],
  285. J2C => ['JP2', 'JPEG 2000 codestream'],
  286. J2K => 'JP2',
  287. JNG => ['PNG', 'JPG Network Graphics'],
  288. JP2 => ['JP2', 'JPEG 2000 file'],
  289. # JP4? - looks like a JPEG but the image data is different
  290. JPC => 'J2C',
  291. JPE => 'JPEG',
  292. JPEG => ['JPEG', 'Joint Photographic Experts Group'],
  293. JPF => 'JP2',
  294. JPG => 'JPEG',
  295. JPM => ['JP2', 'JPEG 2000 compound image'],
  296. JPX => ['JP2', 'JPEG 2000 with extensions'],
  297. JXR => ['TIFF', 'JPEG XR'],
  298. K25 => ['TIFF', 'Kodak DC25 RAW'],
  299. KDC => ['TIFF', 'Kodak Digital Camera RAW'],
  300. KEY => ['ZIP', 'Apple Keynote presentation'],
  301. KTH => ['ZIP', 'Apple Keynote Theme'],
  302. LA => ['RIFF', 'Lossless Audio'],
  303. LFP => ['LFP', 'Lytro Light Field Picture'],
  304. LFR => 'LFP', # (Light Field RAW)
  305. LNK => ['LNK', 'Windows shortcut'],
  306. M2T => 'M2TS',
  307. M2TS => ['M2TS', 'MPEG-2 Transport Stream'],
  308. M2V => ['MPEG', 'MPEG-2 Video'],
  309. M4A => ['MOV', 'MPEG-4 Audio'],
  310. M4B => ['MOV', 'MPEG-4 audio Book'],
  311. M4P => ['MOV', 'MPEG-4 Protected'],
  312. M4V => ['MOV', 'MPEG-4 Video'],
  313. MEF => ['TIFF', 'Mamiya (RAW) Electronic Format'],
  314. MIE => ['MIE', 'Meta Information Encapsulation format'],
  315. MIF => 'MIFF',
  316. MIFF => ['MIFF', 'Magick Image File Format'],
  317. MKA => ['MKV', 'Matroska Audio'],
  318. MKS => ['MKV', 'Matroska Subtitle'],
  319. MKV => ['MKV', 'Matroska Video'],
  320. MNG => ['PNG', 'Multiple-image Network Graphics'],
  321. MOBI => ['PDB', 'Mobipocket electronic book'],
  322. MODD => ['PLIST','Sony Picture Motion metadata'],
  323. MOI => ['MOI', 'MOD Information file'],
  324. MOS => ['TIFF', 'Creo Leaf Mosaic'],
  325. MOV => ['MOV', 'Apple QuickTime movie'],
  326. MP3 => ['MP3', 'MPEG-1 Layer 3 audio'],
  327. MP4 => ['MOV', 'MPEG-4 video'],
  328. MPC => ['MPC', 'Musepack Audio'],
  329. MPEG => ['MPEG', 'MPEG-1 or MPEG-2 audio/video'],
  330. MPG => 'MPEG',
  331. MPO => ['JPEG', 'Extended Multi-Picture format'],
  332. MQV => ['MOV', 'Sony Mobile Quicktime Video'],
  333. MRW => ['MRW', 'Minolta RAW format'],
  334. MTS => 'M2TS',
  335. MXF => ['MXF', 'Material Exchange Format'],
  336. # NDPI => ['TIFF', 'Hamamatsu NanoZoomer Digital Pathology Image'],
  337. NEF => ['TIFF', 'Nikon (RAW) Electronic Format'],
  338. NEWER => 'COS',
  339. NMBTEMPLATE => ['ZIP','Apple Numbers Template'],
  340. NRW => ['TIFF', 'Nikon RAW (2)'],
  341. NUMBERS => ['ZIP','Apple Numbers spreadsheet'],
  342. ODB => ['ZIP', 'Open Document Database'],
  343. ODC => ['ZIP', 'Open Document Chart'],
  344. ODF => ['ZIP', 'Open Document Formula'],
  345. ODG => ['ZIP', 'Open Document Graphics'],
  346. ODI => ['ZIP', 'Open Document Image'],
  347. ODP => ['ZIP', 'Open Document Presentation'],
  348. ODS => ['ZIP', 'Open Document Spreadsheet'],
  349. ODT => ['ZIP', 'Open Document Text file'],
  350. OFR => ['RIFF', 'OptimFROG audio'],
  351. OGG => ['OGG', 'Ogg Vorbis audio file'],
  352. OGV => ['OGG', 'Ogg Video file'],
  353. ORF => ['ORF', 'Olympus RAW format'],
  354. OTF => ['Font', 'Open Type Font'],
  355. PAC => ['RIFF', 'Lossless Predictive Audio Compression'],
  356. PAGES => ['ZIP', 'Apple Pages document'],
  357. PBM => ['PPM', 'Portable BitMap'],
  358. PCD => ['PCD', 'Kodak Photo CD Image Pac'],
  359. PCT => 'PICT',
  360. PDB => ['PDB', 'Palm Database'],
  361. PDF => ['PDF', 'Adobe Portable Document Format'],
  362. PEF => ['TIFF', 'Pentax (RAW) Electronic Format'],
  363. PFA => ['Font', 'PostScript Font ASCII'],
  364. PFB => ['Font', 'PostScript Font Binary'],
  365. PFM => ['Font', 'Printer Font Metrics'],
  366. PGF => ['PGF', 'Progressive Graphics File'],
  367. PGM => ['PPM', 'Portable Gray Map'],
  368. PHP => ['PHP', 'PHP Hypertext Preprocessor'],
  369. PHP3 => 'PHP',
  370. PHP4 => 'PHP',
  371. PHP5 => 'PHP',
  372. PHPS => 'PHP',
  373. PHTML=> 'PHP',
  374. PICT => ['PICT', 'Apple PICTure'],
  375. PLIST=> ['PLIST','Apple Property List'],
  376. PMP => ['PMP', 'Sony DSC-F1 Cyber-Shot PMP'], # should stand for Proprietery Metadata Package ;)
  377. PNG => ['PNG', 'Portable Network Graphics'],
  378. POT => ['FPX', 'Microsoft PowerPoint Template'],
  379. POTM => [['ZIP','FPX'], 'Office Open XML Presentation Template Macro-enabled'],
  380. POTX => [['ZIP','FPX'], 'Office Open XML Presentation Template'],
  381. PPM => ['PPM', 'Portable Pixel Map'],
  382. PPS => ['FPX', 'Microsoft PowerPoint Slideshow'],
  383. PPSM => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow Macro-enabled'],
  384. PPSX => [['ZIP','FPX'], 'Office Open XML Presentation Slideshow'],
  385. PPT => ['FPX', 'Microsoft PowerPoint Presentation'],
  386. PPTM => [['ZIP','FPX'], 'Office Open XML Presentation Macro-enabled'],
  387. PPTX => [['ZIP','FPX'], 'Office Open XML Presentation'],
  388. PRC => ['PDB', 'Palm Database'],
  389. PS => ['PS', 'PostScript'],
  390. PS2 => 'PS',
  391. PS3 => 'PS',
  392. PSB => ['PSD', 'Photoshop Large Document'],
  393. PSD => ['PSD', 'Photoshop Document'],
  394. PSP => ['PSP', 'Paint Shop Pro'],
  395. PSPFRAME => 'PSP',
  396. PSPIMAGE => 'PSP',
  397. PSPSHAPE => 'PSP',
  398. PSPTUBE => 'PSP',
  399. QIF => 'QTIF',
  400. QT => 'MOV',
  401. QTI => 'QTIF',
  402. QTIF => ['QTIF', 'QuickTime Image File'],
  403. RA => ['Real', 'Real Audio'],
  404. RAF => ['RAF', 'FujiFilm RAW Format'],
  405. RAM => ['Real', 'Real Audio Metafile'],
  406. RAR => ['RAR', 'RAR Archive'],
  407. RAW => [['RAW','TIFF'], 'Kyocera Contax N Digital RAW or Panasonic RAW'],
  408. RIF => 'RIFF',
  409. RIFF => ['RIFF', 'Resource Interchange File Format'],
  410. RM => ['Real', 'Real Media'],
  411. RMVB => ['Real', 'Real Media Variable Bitrate'],
  412. RPM => ['Real', 'Real Media Plug-in Metafile'],
  413. RSRC => ['RSRC', 'Mac OS Resource'],
  414. RTF => ['RTF', 'Rich Text Format'],
  415. RV => ['Real', 'Real Video'],
  416. RW2 => ['TIFF', 'Panasonic RAW 2'],
  417. RWL => ['TIFF', 'Leica RAW'],
  418. RWZ => ['RWZ', 'Rawzor compressed image'],
  419. SEQ => ['FLIR', 'FLIR image Sequence'],
  420. SO => ['EXE', 'Shared Object file'],
  421. SR2 => ['TIFF', 'Sony RAW Format 2'],
  422. SRF => ['TIFF', 'Sony RAW Format'],
  423. SRW => ['TIFF', 'Samsung RAW format'],
  424. SVG => ['XMP', 'Scalable Vector Graphics'],
  425. SWF => ['SWF', 'Shockwave Flash'],
  426. TAR => ['TAR', 'TAR archive'],
  427. THM => ['JPEG', 'Canon Thumbnail'],
  428. THMX => [['ZIP','FPX'], 'Office Open XML Theme'],
  429. TIF => 'TIFF',
  430. TIFF => ['TIFF', 'Tagged Image File Format'],
  431. TORRENT => ['Torrent', 'BitTorrent description file'],
  432. TS => 'M2TS',
  433. TTC => ['Font', 'True Type Font Collection'],
  434. TTF => ['Font', 'True Type Font'],
  435. TUB => 'PSP',
  436. VCARD=> ['VCard','Virtual Card'],
  437. VCF => 'VCARD',
  438. VOB => ['MPEG', 'Video Object'],
  439. VRD => ['VRD', 'Canon VRD Recipe Data'],
  440. VSD => ['FPX', 'Microsoft Visio Drawing'],
  441. WAV => ['RIFF', 'WAVeform (Windows digital audio)'],
  442. WDP => ['TIFF', 'Windows Media Photo'],
  443. WEBM => ['MKV', 'Google Web Movie'],
  444. WEBP => ['RIFF', 'Google Web Picture'],
  445. WMA => ['ASF', 'Windows Media Audio'],
  446. WMF => ['WMF', 'Windows Metafile Format'],
  447. WMV => ['ASF', 'Windows Media Video'],
  448. WV => ['RIFF', 'WavePack lossless audio'],
  449. X3F => ['X3F', 'Sigma RAW format'],
  450. XCF => ['XCF', 'GIMP native image format'],
  451. XHTML=> ['HTML', 'Extensible HyperText Markup Language'],
  452. XLA => ['FPX', 'Microsoft Excel Add-in'],
  453. XLAM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Add-in Macro-enabled'],
  454. XLS => ['FPX', 'Microsoft Excel Spreadsheet'],
  455. XLSB => [['ZIP','FPX'], 'Office Open XML Spreadsheet Binary'],
  456. XLSM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Macro-enabled'],
  457. XLSX => [['ZIP','FPX'], 'Office Open XML Spreadsheet'],
  458. XLT => ['FPX', 'Microsoft Excel Template'],
  459. XLTM => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template Macro-enabled'],
  460. XLTX => [['ZIP','FPX'], 'Office Open XML Spreadsheet Template'],
  461. XMP => ['XMP', 'Extensible Metadata Platform'],
  462. ZIP => ['ZIP', 'ZIP archive'],
  463. );
  464. # typical extension for each file type (if different than FileType)
  465. # - case is not significant
  466. my %fileTypeExt = (
  467. 'Canon 1D RAW' => 'tif',
  468. DICOM => 'dcm',
  469. FLIR => 'fff',
  470. GZIP => 'gz',
  471. JPEG => 'jpg',
  472. M2TS => 'mts',
  473. MPEG => 'mpg',
  474. TIFF => 'tif',
  475. VCard => 'vcf',
  476. );
  477. # descriptions for file types not found in above file extension lookup
  478. my %fileDescription = (
  479. DICOM => 'Digital Imaging and Communications in Medicine',
  480. XML => 'Extensible Markup Language',
  481. 'DJVU (multi-page)' => 'DjVu multi-page image',
  482. 'Win32 EXE' => 'Windows 32-bit Executable',
  483. 'Win32 DLL' => 'Windows 32-bit Dynamic Link Library',
  484. 'Win64 EXE' => 'Windows 64-bit Executable',
  485. 'Win64 DLL' => 'Windows 64-bit Dynamic Link Library',
  486. );
  487. # MIME types for applicable file types above
  488. # (missing entries default to 'application/unknown', but note that other MIME
  489. # types may be specified by some modules, eg. QuickTime.pm and RIFF.pm)
  490. %mimeType = (
  491. '3FR' => 'image/x-hasselblad-3fr',
  492. AA => 'audio/audible',
  493. AI => 'application/vnd.adobe.illustrator',
  494. AIFF => 'audio/x-aiff',
  495. APE => 'audio/x-monkeys-audio',
  496. ASF => 'video/x-ms-asf',
  497. ARW => 'image/x-sony-arw',
  498. BMP => 'image/bmp',
  499. BTF => 'image/x-tiff-big', #(NC) (ref http://www.asmail.be/msg0055371937.html)
  500. BZ2 => 'application/bzip2',
  501. 'Canon 1D RAW' => 'image/x-raw', # (uses .TIF file extension)
  502. CHM => 'application/x-chm',
  503. CR2 => 'image/x-canon-cr2',
  504. CRW => 'image/x-canon-crw',
  505. DCR => 'image/x-kodak-dcr',
  506. DEX => 'application/octet-stream',
  507. DFONT=> 'application/x-dfont',
  508. DICOM=> 'application/dicom',
  509. DIVX => 'video/divx',
  510. DJVU => 'image/vnd.djvu',
  511. DNG => 'image/x-adobe-dng',
  512. DOC => 'application/msword',
  513. DOCM => 'application/vnd.ms-word.document.macroEnabled',
  514. DOCX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  515. DOT => 'application/msword',
  516. DOTM => 'application/vnd.ms-word.template.macroEnabledTemplate',
  517. DOTX => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
  518. DPX => 'image/x-dpx',
  519. DS2 => 'audio/x-ds2',
  520. DSS => 'audio/x-dss',
  521. DV => 'video/x-dv',
  522. EIP => 'application/x-captureone', #(NC)
  523. EPS => 'application/postscript',
  524. ERF => 'image/x-epson-erf',
  525. EXE => 'application/octet-stream',
  526. EXV => 'image/x-exv',
  527. FFF => 'image/x-hasselblad-fff',
  528. FLA => 'application/vnd.adobe.fla',
  529. FLAC => 'audio/flac',
  530. FLV => 'video/x-flv',
  531. Font => 'application/x-font-type1', # covers PFA, PFB and PFM (not sure about PFM)
  532. FPX => 'image/vnd.fpx',
  533. GIF => 'image/gif',
  534. GZIP => 'application/x-gzip',
  535. HDP => 'image/vnd.ms-photo',
  536. HDR => 'image/vnd.radiance',
  537. HTML => 'text/html',
  538. ICC => 'application/vnd.iccprofile',
  539. ICS => 'text/calendar',
  540. IDML => 'application/vnd.adobe.indesign-idml-package',
  541. IIQ => 'image/x-raw',
  542. IND => 'application/x-indesign',
  543. INX => 'application/x-indesign-interchange', #PH (NC)
  544. ITC => 'application/itunes',
  545. JNG => 'image/jng',
  546. J2C => 'image/x-j2c', #PH (NC)
  547. JP2 => 'image/jp2',
  548. JPEG => 'image/jpeg',
  549. JPM => 'image/jpm',
  550. JPX => 'image/jpx',
  551. K25 => 'image/x-kodak-k25',
  552. KDC => 'image/x-kodak-kdc',
  553. LFP => 'image/x-lytro-lfp', #PH (NC)
  554. LNK => 'application/octet-stream',
  555. M2T => 'video/mpeg',
  556. M2TS => 'video/m2ts',
  557. MEF => 'image/x-mamiya-mef',
  558. MIE => 'application/x-mie',
  559. MIFF => 'application/x-magick-image',
  560. MKA => 'audio/x-matroska',
  561. MKS => 'application/x-matroska',
  562. MKV => 'video/x-matroska',
  563. MNG => 'video/mng',
  564. MOBI => 'application/x-mobipocket-ebook',
  565. MOS => 'image/x-raw',
  566. MOV => 'video/quicktime',
  567. MP3 => 'audio/mpeg',
  568. MP4 => 'video/mp4',
  569. MPC => 'audio/x-musepack',
  570. MPEG => 'video/mpeg',
  571. MRW => 'image/x-minolta-mrw',
  572. MXF => 'application/mxf',
  573. NEF => 'image/x-nikon-nef',
  574. NRW => 'image/x-nikon-nrw',
  575. ODB => 'application/vnd.oasis.opendocument.database',
  576. ODC => 'application/vnd.oasis.opendocument.chart',
  577. ODF => 'application/vnd.oasis.opendocument.formula',
  578. ODG => 'application/vnd.oasis.opendocument.graphics',
  579. ODI => 'application/vnd.oasis.opendocument.image',
  580. ODP => 'application/vnd.oasis.opendocument.presentation',
  581. ODS => 'application/vnd.oasis.opendocument.spreadsheet',
  582. ODT => 'application/vnd.oasis.opendocument.text',
  583. OGG => 'audio/x-ogg',
  584. OGV => 'video/x-ogg',
  585. EXR => 'image/x-exr',
  586. ORF => 'image/x-olympus-orf',
  587. OTF => 'application/x-font-otf',
  588. PBM => 'image/x-portable-bitmap',
  589. PCD => 'image/x-photo-cd',
  590. PDB => 'application/vnd.palm',
  591. PDF => 'application/pdf',
  592. PEF => 'image/x-pentax-pef',
  593. PFA => 'application/x-font-type1', # (needed if handled by PostScript module)
  594. PGF => 'image/pgf',
  595. PGM => 'image/x-portable-graymap',
  596. PHP => 'application/x-httpd-php',
  597. PICT => 'image/pict',
  598. PLIST=> 'application/xml', # (binary PLIST format is 'application/x-plist', recognized at run time)
  599. PNG => 'image/png',
  600. POT => 'application/vnd.ms-powerpoint',
  601. POTM => 'application/vnd.ms-powerpoint.template.macroEnabled',
  602. POTX => 'application/vnd.openxmlformats-officedocument.presentationml.template',
  603. PPM => 'image/x-portable-pixmap',
  604. PPS => 'application/vnd.ms-powerpoint',
  605. PPSM => 'application/vnd.ms-powerpoint.slideshow.macroEnabled',
  606. PPSX => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
  607. PPT => 'application/vnd.ms-powerpoint',
  608. PPTM => 'application/vnd.ms-powerpoint.presentation.macroEnabled',
  609. PPTX => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  610. PS => 'application/postscript',
  611. PSD => 'application/vnd.adobe.photoshop',
  612. PSP => 'image/x-paintshoppro', #(NC)
  613. QTIF => 'image/x-quicktime',
  614. RA => 'audio/x-pn-realaudio',
  615. RAF => 'image/x-fujifilm-raf',
  616. RAM => 'audio/x-pn-realaudio',
  617. RAR => 'application/x-rar-compressed',
  618. RAW => 'image/x-raw',
  619. RM => 'application/vnd.rn-realmedia',
  620. RMVB => 'application/vnd.rn-realmedia-vbr',
  621. RPM => 'audio/x-pn-realaudio-plugin',
  622. RSRC => 'application/ResEdit',
  623. RTF => 'text/rtf',
  624. RV => 'video/vnd.rn-realvideo',
  625. RW2 => 'image/x-panasonic-rw2',
  626. RWL => 'image/x-leica-rwl',
  627. RWZ => 'image/x-rawzor', #(duplicated in Rawzor.pm)
  628. SR2 => 'image/x-sony-sr2',
  629. SRF => 'image/x-sony-srf',
  630. SRW => 'image/x-samsung-srw',
  631. SVG => 'image/svg+xml',
  632. SWF => 'application/x-shockwave-flash',
  633. TAR => 'application/x-tar',
  634. THMX => 'application/vnd.ms-officetheme',
  635. TIFF => 'image/tiff',
  636. Torrent => 'application/x-bittorrent',
  637. TTC => 'application/x-font-ttf',
  638. TTF => 'application/x-font-ttf',
  639. VCard=> 'text/vcard',
  640. VSD => 'application/x-visio',
  641. WDP => 'image/vnd.ms-photo',
  642. WEBM => 'video/webm',
  643. WMA => 'audio/x-ms-wma',
  644. WMF => 'application/x-wmf',
  645. WMV => 'video/x-ms-wmv',
  646. X3F => 'image/x-sigma-x3f',
  647. XCF => 'image/x-xcf',
  648. XLA => 'application/vnd.ms-excel',
  649. XLAM => 'application/vnd.ms-excel.addin.macroEnabled',
  650. XLS => 'application/vnd.ms-excel',
  651. XLSB => 'application/vnd.ms-excel.sheet.binary.macroEnabled',
  652. XLSM => 'application/vnd.ms-excel.sheet.macroEnabled',
  653. XLSX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  654. XLT => 'application/vnd.ms-excel',
  655. XLTM => 'application/vnd.ms-excel.template.macroEnabled',
  656. XLTX => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
  657. XML => 'application/xml',
  658. XMP => 'application/rdf+xml',
  659. ZIP => 'application/zip',
  660. );
  661. # module names for processing routines of each file type
  662. # - undefined entries default to same module name as file type
  663. # - module name '' defaults to Image::ExifTool
  664. # - module name '0' indicates a recognized but unsupported file
  665. my %moduleName = (
  666. AA => 'Audible',
  667. AVC => 0,
  668. BTF => 'BigTIFF',
  669. BZ2 => 0,
  670. CRW => 'CanonRaw',
  671. CHM => 'EXE',
  672. COS => 'CaptureOne',
  673. DEX => 0,
  674. DOCX => 'OOXML',
  675. DR4 => 'CanonVRD',
  676. DSS => 'Olympus',
  677. EPS => 'PostScript',
  678. EXIF => '',
  679. EXR => 'OpenEXR',
  680. EXV => '',
  681. ICC => 'ICC_Profile',
  682. IND => 'InDesign',
  683. FLV => 'Flash',
  684. FPF => 'FLIR',
  685. FPX => 'FlashPix',
  686. GZIP => 'ZIP',
  687. HDR => 'Radiance',
  688. JP2 => 'Jpeg2000',
  689. JPEG => '',
  690. LFP => 'Lytro',
  691. MOV => 'QuickTime',
  692. MKV => 'Matroska',
  693. MP3 => 'ID3',
  694. MRW => 'MinoltaRaw',
  695. OGG => 'Ogg',
  696. ORF => 'Olympus',
  697. PDB => 'Palm',
  698. PCD => 'PhotoCD',
  699. PHP => 0,
  700. PMP => 'Sony',
  701. PS => 'PostScript',
  702. PSD => 'Photoshop',
  703. QTIF => 'QuickTime',
  704. RAF => 'FujiFilm',
  705. RAR => 'ZIP',
  706. RAW => 'KyoceraRaw',
  707. RWZ => 'Rawzor',
  708. SWF => 'Flash',
  709. TAR => 0,
  710. TIFF => '',
  711. VRD => 'CanonVRD',
  712. WMF => 0,
  713. X3F => 'SigmaRaw',
  714. XCF => 'GIMP',
  715. );
  716. # quick "magic number" file test used to avoid loading module unnecessarily:
  717. # - regular expression evaluated on first 1024 bytes of file
  718. # - must match beginning at first byte in file
  719. # - this test must not be more stringent than module logic
  720. %magicNumber = (
  721. AA => '.{4}\x57\x90\x75\x36',
  722. AIFF => '(FORM....AIF[FC]|AT&TFORM)',
  723. APE => '(MAC |APETAGEX|ID3)',
  724. ASF => '\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c',
  725. AVC => '\+A\+V\+C\+',
  726. Torrent => 'd\d+:\w+',
  727. BMP => 'BM',
  728. BTF => '(II\x2b\0|MM\0\x2b)',
  729. BZ2 => 'BZh[1-9]\x31\x41\x59\x26\x53\x59',
  730. CHM => 'ITSF.{20}\x10\xfd\x01\x7c\xaa\x7b\xd0\x11\x9e\x0c\0\xa0\xc9\x22\xe6\xec',
  731. CRW => '(II|MM).{4}HEAP(CCDR|JPGM)',
  732. DEX => "dex\n035\0",
  733. DICOM=> '(.{128}DICM|\0[\x02\x04\x06\x08]\0[\0-\x20]|[\x02\x04\x06\x08]\0[\0-\x20]\0)',
  734. DOCX => 'PK\x03\x04',
  735. DPX => '(SDPX|XPDS)',
  736. DR4 => 'IIII\x04\0\x04\0',
  737. DSS => '(\x02dss|\x03ds2)',
  738. DV => '\x1f\x07\0[\x3f\xbf]', # (not tested if extension recognized)
  739. EPS => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)',
  740. EXE => '(MZ|\xca\xfe\xba\xbe|\xfe\xed\xfa[\xce\xcf]|[\xce\xcf]\xfa\xed\xfe|Joy!peff|\x7fELF|#!\s*/\S*bin/|!<arch>\x0a)',
  741. EXIF => '(II\x2a\0|MM\0\x2a)',
  742. EXR => '\x76\x2f\x31\x01',
  743. EXV => '\xff\x01Exiv2',
  744. FLAC => '(fLaC|ID3)',
  745. FLIR => '[AF]FF\0',
  746. FLV => 'FLV\x01',
  747. Font => '((\0\x01\0\0|OTTO|true|typ1)[\0\x01]|ttcf\0[\x01\x02]\0\0|\0[\x01\x02]|' .
  748. '(.{6})?%!(PS-(AdobeFont-|Bitstream )|FontType1-)|Start(Comp|Master)?FontMetrics)',
  749. FPF => 'FPF Public Image Format\0',
  750. FPX => '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1',
  751. GIF => 'GIF8[79]a',
  752. GZIP => '\x1f\x8b\x08',
  753. HDR => '#\?(RADIANCE|RGBE)\x0a',
  754. HTML => '(\xef\xbb\xbf)?\s*(?i)<(!DOCTYPE\s+HTML|HTML|\?xml)', # (case insensitive)
  755. ICC => '.{12}(scnr|mntr|prtr|link|spac|abst|nmcl|nkpf)(XYZ |Lab |Luv |YCbr|Yxy |RGB |GRAY|HSV |HLS |CMYK|CMY |[2-9A-F]CLR){2}',
  756. IND => '\x06\x06\xed\xf5\xd8\x1d\x46\xe5\xbd\x31\xef\xe7\xfe\x74\xb7\x1d',
  757. ITC => '.{4}itch',
  758. JP2 => '(\0\0\0\x0cjP( |\x1a\x1a)\x0d\x0a\x87\x0a|\xff\x4f\xff\x51\0)',
  759. JPEG => '\xff(\xd8\xff|\x01Exiv2)', # (includes EXV so we don't have to add EXV to @fileTypes)
  760. LFP => '\x89LFP\x0d\x0a\x1a\x0a',
  761. LNK => '.{4}\x01\x14\x02\0{5}\xc0\0{6}\x46',
  762. M2TS => '(....)?\x47',
  763. MIE => '~[\x10\x18]\x04.0MIE',
  764. MIFF => 'id=ImageMagick',
  765. MKV => '\x1a\x45\xdf\xa3',
  766. MOV => '.{4}(free|skip|wide|ftyp|pnot|PICT|pict|moov|mdat|junk|uuid)', # (duplicated in WriteQuickTime.pl !!)
  767. # MP3 => difficult to rule out
  768. MPC => '(MP\+|ID3)',
  769. MOI => 'V6',
  770. MPEG => '\0\0\x01[\xb0-\xbf]',
  771. MRW => '\0MR[MI]',
  772. MXF => '\x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02', # (not tested if extension recognized)
  773. OGG => '(OggS|ID3)',
  774. ORF => '(II|MM)',
  775. PDB => '.{60}(\.pdfADBE|TEXtREAd|BVokBDIC|DB99DBOS|PNRdPPrs|DataPPrs|vIMGView|PmDBPmDB|InfoINDB|ToGoToGo|SDocSilX|JbDbJBas|JfDbJFil|DATALSdb|Mdb1Mdb1|BOOKMOBI|DataPlkr|DataSprd|SM01SMem|TEXtTlDc|InfoTlIf|DataTlMl|DataTlPt|dataTDBP|TdatTide|ToRaTRPW|zTXTGPlm|BDOCWrdS)',
  776. # PCD => signature is at byte 2048
  777. PDF => '%PDF-\d+\.\d+',
  778. PGF => 'PGF',
  779. PHP => '<\?php\s',
  780. PICT => '(.{10}|.{522})(\x11\x01|\x00\x11)',
  781. PLIST=> '(bplist0|\s*<|\xfe\xff\x00)',
  782. PMP => '.{8}\0{3}\x7c.{112}\xff\xd8\xff\xdb',
  783. PNG => '(\x89P|\x8aM|\x8bJ)NG\r\n\x1a\n',
  784. PPM => 'P[1-6]\s+',
  785. PS => '(%!PS|%!Ad|\xc5\xd0\xd3\xc6)',
  786. PSD => '8BPS\0[\x01\x02]',
  787. PSP => 'Paint Shop Pro Image File\x0a\x1a\0{5}',
  788. QTIF => '.{4}(idsc|idat|iicc)',
  789. RAF => 'FUJIFILM',
  790. RAR => 'Rar!\x1a\x07\0',
  791. RAW => '(.{25}ARECOYK|II|MM)',
  792. Real => '(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)',
  793. RIFF => '(RIFF|LA0[234]|OFR |LPAC|wvpk)', # RIFF plus other variants
  794. RSRC => '(....)?\0\0\x01\0',
  795. RTF => '[\n\r]*\\{[\n\r]*\\\\rtf',
  796. RWZ => 'rawzor',
  797. SWF => '[FC]WS[^\0]',
  798. TAR => '.{257}ustar( )?\0', # (this doesn't catch old-style tar files)
  799. TIFF => '(II|MM)', # don't test magic number (some raw formats are different)
  800. VCard=> '(?i)BEGIN:(VCARD|VCALENDAR)\r\n',
  801. VRD => 'CANON OPTIONAL DATA\0',
  802. WMF => '(\xd7\xcd\xc6\x9a\0\0|\x01\0\x09\0\0\x03)',
  803. X3F => 'FOVb',
  804. XCF => 'gimp xcf ',
  805. XMP => '\0{0,3}(\xfe\xff|\xff\xfe|\xef\xbb\xbf)?\0{0,3}\s*<',
  806. ZIP => 'PK\x03\x04',
  807. );
  808. # file types with weak magic number recognition
  809. my %weakMagic = ( MP3 => 1 );
  810. # file types that are determined by the process proc when FastScan == 3
  811. # (when done, the process proc must exit after SetFileType if FastScan is 3)
  812. my %processType = map { $_ => 1 } qw(JPEG TIFF XMP AIFF EXE Font PS Real VCard);
  813. # lookup for valid character set names (keys are all lower case)
  814. %charsetName = (
  815. # Charset setting alias(es)
  816. # ------------------------- --------------------------------------------
  817. utf8 => 'UTF8', cp65001 => 'UTF8', 'utf-8' => 'UTF8',
  818. latin => 'Latin', cp1252 => 'Latin', latin1 => 'Latin',
  819. latin2 => 'Latin2', cp1250 => 'Latin2',
  820. cyrillic => 'Cyrillic', cp1251 => 'Cyrillic', russian => 'Cyrillic',
  821. greek => 'Greek', cp1253 => 'Greek',
  822. turkish => 'Turkish', cp1254 => 'Turkish',
  823. hebrew => 'Hebrew', cp1255 => 'Hebrew',
  824. arabic => 'Arabic', cp1256 => 'Arabic',
  825. baltic => 'Baltic', cp1257 => 'Baltic',
  826. vietnam => 'Vietnam', cp1258 => 'Vietnam',
  827. thai => 'Thai', cp874 => 'Thai',
  828. macroman => 'MacRoman', cp10000 => 'MacRoman', mac => 'MacRoman', roman => 'MacRoman',
  829. maclatin2 => 'MacLatin2', cp10029 => 'MacLatin2',
  830. maccyrillic => 'MacCyrillic', cp10007 => 'MacCyrillic',
  831. macgreek => 'MacGreek', cp10006 => 'MacGreek',
  832. macturkish => 'MacTurkish', cp10081 => 'MacTurkish',
  833. macromanian => 'MacRomanian', cp10010 => 'MacRomanian',
  834. maciceland => 'MacIceland', cp10079 => 'MacIceland',
  835. maccroatian => 'MacCroatian', cp10082 => 'MacCroatian',
  836. );
  837. # default group priority for writing
  838. # (NOTE: tags in groups not specified here will not be written unless
  839. # overridden by the module or specified when writing)
  840. my @defaultWriteGroups = qw(EXIF IPTC XMP MakerNotes Photoshop ICC_Profile CanonVRD Adobe);
  841. # group hash for ExifTool-generated tags
  842. my %allGroupsExifTool = ( 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'ExifTool' );
  843. # special tag names (not used for tag info)
  844. %specialTags = map { $_ => 1 } qw(
  845. TABLE_NAME SHORT_NAME PROCESS_PROC WRITE_PROC CHECK_PROC
  846. GROUPS FORMAT FIRST_ENTRY TAG_PREFIX PRINT_CONV
  847. WRITABLE TABLE_DESC NOTES IS_OFFSET IS_SUBDIR
  848. EXTRACT_UNKNOWN NAMESPACE PREFERRED SRC_TABLE PRIORITY
  849. WRITE_GROUP LANG_INFO VARS DATAMEMBER SET_GROUP1
  850. );
  851. # headers for various segment types
  852. $exifAPP1hdr = "Exif\0\0";
  853. $xmpAPP1hdr = "http://ns.adobe.com/xap/1.0/\0";
  854. $xmpExtAPP1hdr = "http://ns.adobe.com/xmp/extension/\0";
  855. $psAPP13hdr = "Photoshop 3.0\0";
  856. $psAPP13old = 'Adobe_Photoshop2.5:';
  857. sub DummyWriteProc { return 1; }
  858. # lookup for user lenses defined in @Image::ExifTool::UserDefined::Lenses
  859. %Image::ExifTool::userLens = ( );
  860. # queued plug-in tags to add to lookup
  861. @Image::ExifTool::pluginTags = ( );
  862. %Image::ExifTool::pluginTags = ( );
  863. my %systemTagsNotes = (
  864. Notes => q{
  865. extracted only if specifically requested or the SystemTags or RequestAll API
  866. option is set
  867. },
  868. );
  869. # tag information for preview image -- this should be used for all
  870. # PreviewImage tags so they are handled properly when reading/writing
  871. %Image::ExifTool::previewImageTagInfo = (
  872. Name => 'PreviewImage',
  873. Writable => 'undef',
  874. # a value of 'none' is ok...
  875. WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)',
  876. DataTag => 'PreviewImage',
  877. # accept either scalar or scalar reference
  878. RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
  879. # we allow preview image to be set to '', but we don't want a zero-length value
  880. # in the IFD, so set it temorarily to 'none'. Note that the length is <= 4,
  881. # so this value will fit in the IFD so the preview fixup won't be generated.
  882. ValueConvInv => '$val eq "" and $val="none"; $val',
  883. );
  884. # extra tags that aren't truly EXIF tags, but are generated by the script
  885. # Note: any tag in this list with a name corresponding to a Group0 name is
  886. # used to write the entire corresponding directory as a block.
  887. %Image::ExifTool::Extra = (
  888. GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
  889. VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags
  890. WRITE_PROC => \&DummyWriteProc,
  891. Error => {
  892. Priority => 0,
  893. Groups => \%allGroupsExifTool,
  894. Notes => q{
  895. returns errors that may have occurred while reading or writing a file. Any
  896. Error will prevent the file from being processed. Minor errors may be
  897. downgraded to warnings with the -m or IgnoreMinorErrors option
  898. },
  899. },
  900. Warning => {
  901. Priority => 0,
  902. Groups => \%allGroupsExifTool,
  903. Notes => q{
  904. returns warnings that may have occurred while reading or writing a file.
  905. Use the -a or Duplicates option to see all warnings if more than one
  906. occurred. Minor warnings may be ignored with the -m or IgnoreMinorErrors
  907. option. Minor warnings with a capital "M" in the "[Minor]" designation
  908. indicate that the processing is affected by ignoring the warning
  909. },
  910. },
  911. Comment => {
  912. Notes => 'comment embedded in JPEG, GIF89a or PPM/PGM/PBM image',
  913. Writable => 1,
  914. WriteGroup => 'Comment',
  915. Priority => 0, # to preserve order of JPEG COM segments
  916. },
  917. Directory => {
  918. Groups => { 1 => 'System' },
  919. Notes => q{
  920. the directory of the file as specified in the call to ExifTool, or "." if no
  921. directory was specified. May be written to move the file to another
  922. directory that will be created if doesn't already exist
  923. },
  924. Writable => 1,
  925. Protected => 1,
  926. RawConv => '$self->ConvertFileName($val)',
  927. # translate backslashes in directory names and add trailing '/'
  928. ValueConvInv => '$_ = $self->InverseFileName($val); m{[^/]$} and $_ .= "/"; $_',
  929. },
  930. FileName => {
  931. Groups => { 1 => 'System' },
  932. Writable => 1,
  933. Protected => 1,
  934. Notes => q{
  935. may be written with a full path name to set FileName and Directory in one
  936. operation. This is such a powerful feature that a TestName tag is provided
  937. to allow dry-run tests before actually writing the file name. See
  938. L<filename.html|../filename.html> for more information on writing the
  939. FileName, Directory and TestName tags
  940. },
  941. RawConv => '$self->ConvertFileName($val)',
  942. ValueConvInv => '$self->InverseFileName($val)',
  943. },
  944. FilePath => {
  945. Groups => { 1 => 'System' },
  946. Notes => q{
  947. absolute path of source file. Not generated unless specifically requested or
  948. the RequestAll API option is set
  949. },
  950. },
  951. TestName => {
  952. Writable => 1,
  953. WriteOnly => 1,
  954. Notes => q{
  955. this write-only tag may be used instead of FileName for dry-run tests of the
  956. file renaming feature. Writing this tag prints the old and new file names
  957. to the console, but does not affect the file itself
  958. },
  959. ValueConvInv => '$self->InverseFileName($val)',
  960. },
  961. FileSequence => {
  962. Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Other' },
  963. Notes => q{
  964. sequence number for each source file when extracting or copying information,
  965. including files that fail the -if condition of the command-line application,
  966. beginning at 0 for the first file. Not generated unless specifically
  967. requested or the RequestAll API option is set
  968. },
  969. },
  970. FileSize => {
  971. Groups => { 1 => 'System' },
  972. Notes => q{
  973. note that the print conversion for this tag uses historic prefixes: 1 kB =
  974. 1024 bytes, etc.
  975. },
  976. PrintConv => \&ConvertFileSize,
  977. },
  978. ResourceForkSize => {
  979. Groups => { 1 => 'System' },
  980. Notes => q{
  981. size of the file's resource fork if it contains data. Mac OS only. If this
  982. tag is generated the ExtractEmbedded option may be used to extract
  983. resource-fork information as a sub-document. When writing, the resource
  984. fork is preserved by default, but it may be deleted with C<-rsrc:all=> on
  985. the command line
  986. },
  987. PrintConv => \&ConvertFileSize,
  988. },
  989. FileType => {
  990. Notes => q{
  991. a short description of the file type. For many file types this is the just
  992. the uppercase file extension
  993. },
  994. },
  995. FileTypeExtension => {
  996. Notes => q{
  997. a common lowercase extension for this file type, or uppercase with the -n
  998. option
  999. },
  1000. PrintConv => 'lc $val',
  1001. },
  1002. FileModifyDate => {
  1003. Description => 'File Modification Date/Time',
  1004. Notes => q{
  1005. the filesystem modification date/time. Note that ExifTool may not be able
  1006. to handle filesystem dates before 1970 depending on the limitations of the
  1007. system's standard libraries
  1008. },
  1009. Groups => { 1 => 'System', 2 => 'Time' },
  1010. Writable => 1,
  1011. # all writable pseudo-tags must be protected so -tagsfromfile fails with
  1012. # unrecognized files unless a pseudo tag is specified explicitly
  1013. Protected => 1,
  1014. Shift => 'Time',
  1015. ValueConv => 'ConvertUnixTime($val,1)',
  1016. ValueConvInv => 'GetUnixTime($val,1)',
  1017. PrintConv => '$self->ConvertDateTime($val)',
  1018. PrintConvInv => '$self->InverseDateTime($val)',
  1019. },
  1020. FileAccessDate => {
  1021. Description => 'File Access Date/Time',
  1022. Notes => q{
  1023. the date/time of last access of the file. Note that this access time is
  1024. updated whenever any software, including ExifTool, reads the file
  1025. },
  1026. Groups => { 1 => 'System', 2 => 'Time' },
  1027. ValueConv => 'ConvertUnixTime($val,1)',
  1028. PrintConv => '$self->ConvertDateTime($val)',
  1029. },
  1030. FileCreateDate => {
  1031. Description => 'File Creation Date/Time',
  1032. Notes => q{
  1033. the filesystem creation date/time. Windows only. Requires Win32API::File
  1034. and Win32::API for writing. Note that although ExifTool does not currently
  1035. support FileCreateDate on other systems, the OS X file creation date may be
  1036. accessed via the MDItemFSCreationDate tag when the MDItemTags API option is
  1037. set, and may be written indirectly via the FileModifyDate tag by following
  1038. these steps: 1. Rewrite the file to set the filesystem creation and
  1039. modification times to the current time, 2. Set FileModifyDate to the desired
  1040. creation time, then 3. Restore FileModifyDate to its original value
  1041. },
  1042. Groups => { 1 => 'System', 2 => 'Time' },
  1043. Writable => 1,
  1044. Protected => 1, # all writable pseudo-tags must be protected!
  1045. Shift => 'Time',
  1046. ValueConv => 'ConvertUnixTime($val,1)',
  1047. ValueConvInv => q{
  1048. if ($^O ne 'MSWin32') {
  1049. warn "This tag is Windows only\n";
  1050. return undef;
  1051. }
  1052. return GetUnixTime($val,1);
  1053. },
  1054. PrintConv => '$self->ConvertDateTime($val)',
  1055. PrintConvInv => '$self->InverseDateTime($val)',
  1056. },
  1057. FileInodeChangeDate => {
  1058. Description => 'File Inode Change Date/Time',
  1059. Notes => q{
  1060. the date/time when the file's directory information was last changed.
  1061. Non-Windows systems only
  1062. },
  1063. Groups => { 1 => 'System', 2 => 'Time' },
  1064. ValueConv => 'ConvertUnixTime($val,1)',
  1065. PrintConv => '$self->ConvertDateTime($val)',
  1066. },
  1067. FilePermissions => {
  1068. Groups => { 1 => 'System' },
  1069. Notes => q{
  1070. r=read, w=write and x=execute permissions for the file owner, group and
  1071. others. The ValueConv value is an octal number so bit test operations on
  1072. this value should be done in octal, eg. 'oct($filePermissions#) & 0200'
  1073. },
  1074. ValueConv => 'sprintf("%.3o", $val & 0777)',
  1075. PrintConv => sub {
  1076. my ($mask, $str, $val) = (0400, '', oct(shift));
  1077. while ($mask) {
  1078. foreach (qw(r w x)) {
  1079. $str .= $val & $mask ? $_ : '-';
  1080. $mask >>= 1;
  1081. }
  1082. }
  1083. return $str;
  1084. },
  1085. },
  1086. FileAttributes => {
  1087. Groups => { 1 => 'System' },
  1088. Notes => q{
  1089. extracted only if specifically requested or the SystemTags or RequestAll API
  1090. option is set. 2 or 3 values: 0. File type, 1. Attribute bits, 2. Windows
  1091. attribute bits if Win32API::File is available
  1092. },
  1093. PrintHex => 1,
  1094. PrintConvColumns => 2,
  1095. PrintConv => [{ # stat device types (bitmask 0xf000)
  1096. 0x0000 => 'Unknown',
  1097. 0x1000 => 'FIFO',
  1098. 0x2000 => 'Character',
  1099. 0x3000 => 'Mux Character',
  1100. 0x4000 => 'Directory',
  1101. 0x5000 => 'XENIX Named',
  1102. 0x6000 => 'Block',
  1103. 0x7000 => 'Mux Block',
  1104. 0x8000 => 'Regular',
  1105. 0x9000 => 'VxFS Compressed',
  1106. 0xa000 => 'Symbolic Link',
  1107. 0xb000 => 'Solaris Shadow Inode',
  1108. 0xc000 => 'Socket',
  1109. 0xd000 => 'Solaris Door',
  1110. 0xe000 => 'BSD Whiteout',
  1111. },{ BITMASK => { # stat attribute bits (bitmask 0x0e00)
  1112. 9 => 'Sticky',
  1113. 10 => 'Set Group ID',
  1114. 11 => 'Set User ID',
  1115. }},{ BITMASK => { # Windows attribute bits
  1116. 0 => 'Read Only',
  1117. 1 => 'Hidden',
  1118. 2 => 'System',
  1119. 3 => 'Volume Label',
  1120. 4 => 'Directory',
  1121. 5 => 'Archive',
  1122. 6 => 'Device',
  1123. 7 => 'Normal',
  1124. 8 => 'Temporary',
  1125. 9 => 'Sparse File',
  1126. 10 => 'Reparse Point',
  1127. 11 => 'Compressed',
  1128. 12 => 'Offline',
  1129. 13 => 'Not Content Indexed',
  1130. 14 => 'Encrypted',
  1131. }}],
  1132. },
  1133. FileDeviceID => {
  1134. Groups => { 1 => 'System' },
  1135. %systemTagsNotes,
  1136. PrintConv => '(($val >> 24) & 0xff) . "." . ($val & 0xffffff)', # (major.minor)
  1137. },
  1138. FileDeviceNumber => { Groups => { 1 => 'System' }, %systemTagsNotes },
  1139. FileInodeNumber => { Groups => { 1 => 'System' }, %systemTagsNotes },
  1140. FileHardLinks => { Groups => { 1 => 'System' }, %systemTagsNotes },
  1141. FileUserID => { Groups => { 1 => 'System' }, %systemTagsNotes, PrintConv => 'eval { getpwuid($val) } || $val' },
  1142. FileGroupID => { Groups => { 1 => 'System' }, %systemTagsNotes, PrintConv => 'eval { getgrgid($val) } || $val' },
  1143. FileBlockSize => { Groups => { 1 => 'System' }, %systemTagsNotes },
  1144. FileBlockCount => { Groups => { 1 => 'System' }, %systemTagsNotes },
  1145. HardLink => {
  1146. Writable => 1,
  1147. WriteOnly => 1,
  1148. Protected => 1,
  1149. Notes => q{
  1150. this write-only tag is used to create a hard link to the file. If the file
  1151. is edited, copied, renamed or moved in the same operation as writing
  1152. HardLink, then the link is made to the updated file. Note that subsequent
  1153. editing of either the linked file or the original by the exiftool
  1154. application will break the link unless the -overwrite_original_in_place
  1155. option is used
  1156. },
  1157. ValueConvInv => '$val=~tr/\\\\/\//; $val',
  1158. },
  1159. MIMEType => { Notes => 'the MIME type of the source file' },
  1160. ImageWidth => { Notes => 'the width of the image in number of pixels' },
  1161. ImageHeight => { Notes => 'the height of the image in number of pixels' },
  1162. XResolution => { Notes => 'the horizontal pixel resolution' },
  1163. YResolution => { Notes => 'the vertical pixel resolution' },
  1164. MaxVal => { Notes => 'maximum pixel value in PPM or PGM image' },
  1165. EXIF => {
  1166. Notes => 'the full EXIF data block from JPEG, PNG, JP2, MIE and MIFF images',
  1167. Groups => { 0 => 'EXIF', 1 => 'EXIF' },
  1168. Flags => ['Writable' ,'Protected', 'Binary'],
  1169. WriteCheck => q{
  1170. return undef if $val =~ /^(II\x2a\0|MM\0\x2a)/;
  1171. return 'Invalid EXIF data';
  1172. },
  1173. },
  1174. IPTC => {
  1175. Notes => 'the full IPTC data block',
  1176. Groups => { 0 => 'IPTC', 1 => 'IPTC' },
  1177. Flags => ['Writable', 'Protected', 'Binary'],
  1178. Priority => 0, # so main IPTC (which hopefully comes first) takes priority
  1179. WriteCheck => q{
  1180. return undef if $val =~ /^(\x1c|\0+$)/;
  1181. return 'Invalid IPTC data';
  1182. },
  1183. },
  1184. XMP => {
  1185. Notes => q{
  1186. the XMP data block, but note that extended XMP in JPEG images may be split
  1187. into multiple blocks
  1188. },
  1189. Groups => { 0 => 'XMP', 1 => 'XMP' },
  1190. Flags => ['Writable', 'Protected', 'Binary'],
  1191. Priority => 0, # so main xmp (which usually comes first) takes priority
  1192. WriteCheck => q{
  1193. require Image::ExifTool::XMP;
  1194. return Image::ExifTool::XMP::CheckXMP($self, $tagInfo, \$val);
  1195. },
  1196. },
  1197. ICC_Profile => {
  1198. Notes => 'the full ICC_Profile data block',
  1199. Groups => { 0 => 'ICC_Profile', 1 => 'ICC_Profile' },
  1200. Flags => ['Writable' ,'Protected', 'Binary'],
  1201. WriteCheck => q{
  1202. require Image::ExifTool::ICC_Profile;
  1203. return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
  1204. },
  1205. },
  1206. CanonVRD => {
  1207. Notes => 'the full Canon DPP VRD trailer block',
  1208. Groups => { 0 => 'CanonVRD', 1 => 'CanonVRD' },
  1209. Flags => ['Writable' ,'Protected', 'Binary'],
  1210. Permanent => 0, # (this is 1 by default for MakerNotes tags)
  1211. WriteCheck => q{
  1212. return undef if $val =~ /^CANON OPTIONAL DATA\0/;
  1213. return 'Invalid CanonVRD data';
  1214. },
  1215. },
  1216. CanonDR4 => {
  1217. Notes => 'the full Canon DPP version 4 DR4 block',
  1218. Groups => { 0 => 'CanonVRD', 1 => 'CanonVRD' },
  1219. Flags => ['Writable' ,'Protected', 'Binary'],
  1220. Permanent => 0, # (this is 1 by default for MakerNotes tags)
  1221. WriteCheck => q{
  1222. return undef if $val =~ /^IIII\x04\0\x04\0/;
  1223. return 'Invalid CanonDR4 data';
  1224. },
  1225. },
  1226. Adobe => {
  1227. Notes => q{
  1228. the JPEG APP14 Adobe segment. Extracted only if specified. See the
  1229. L<JPEG Adobe Tags|JPEG.html#Adobe> for more information
  1230. },
  1231. Groups => { 0 => 'APP14', 1 => 'Adobe' },
  1232. WriteGroup => 'Adobe',
  1233. Flags => ['Writable' ,'Protected', 'Binary'],
  1234. },
  1235. CurrentIPTCDigest => {
  1236. Notes => q{
  1237. MD5 digest of existing IPTC data. All zeros if IPTC exists but Digest::MD5
  1238. is not installed. Only calculated for IPTC in the standard location as
  1239. specified by the L<MWG|http://www.metadataworkinggroup.org/>. ExifTool
  1240. automates the handling of this tag in the MWG module -- see the
  1241. L<MWG Composite Tags|MWG.html> for details
  1242. },
  1243. ValueConv => 'unpack("H*", $val)',
  1244. },
  1245. PreviewImage => {
  1246. Notes => 'JPEG-format embedded preview image',
  1247. Groups => { 2 => 'Preview' },
  1248. Writable => 1,
  1249. WriteCheck => '$self->CheckImage(\$val)',
  1250. # can't delete, so set to empty string and return no error
  1251. DelCheck => '$val = ""; return undef',
  1252. # accept either scalar or scalar reference
  1253. RawConv => '$self->ValidateImage(ref $val ? $val : \$val, $tag)',
  1254. },
  1255. PreviewPNG => {
  1256. Groups => { 2 => 'Preview' },
  1257. Notes => 'PNG-format embedded preview image',
  1258. Binary => 1,
  1259. },
  1260. PreviewWMF => {
  1261. Groups => { 2 => 'Preview' },
  1262. Notes => 'WMF-format embedded preview image',
  1263. Binary => 1,
  1264. },
  1265. ExifByteOrder => {
  1266. Writable => 1,
  1267. Notes => q{
  1268. represents the byte order of EXIF information. May be written to set the
  1269. byte order only for newly created EXIF segments
  1270. },
  1271. PrintConv => {
  1272. II => 'Little-endian (Intel, II)',
  1273. MM => 'Big-endian (Motorola, MM)',
  1274. },
  1275. },
  1276. ExifUnicodeByteOrder => {
  1277. Writable => 1,
  1278. WriteOnly => 1,
  1279. Notes => q{
  1280. specifies the byte order to use when writing EXIF Unicode text. The EXIF
  1281. specification is particularly vague about this byte ordering, and different
  1282. applications use different conventions. By default ExifTool writes Unicode
  1283. text in EXIF byte order, but this write-only tag may be used to force a
  1284. specific order
  1285. },
  1286. PrintConv => {
  1287. II => 'Little-endian (Intel, II)',
  1288. MM => 'Big-endian (Motorola, MM)',
  1289. },
  1290. },
  1291. ExifToolVersion => {
  1292. Description => 'ExifTool Version Number',
  1293. Groups => \%allGroupsExifTool,
  1294. Notes => 'the version of ExifTool currently running',
  1295. },
  1296. ProcessingTime => {
  1297. Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Other' },
  1298. Notes => q{
  1299. the clock time in seconds taken by ExifTool to extract information from this
  1300. file. Not generated unless specifically requested or the RequestAll API
  1301. option is set. Requires Time::HiRes
  1302. },
  1303. PrintConv => 'sprintf("%.3f s", $val)',
  1304. },
  1305. RAFVersion => { Notes => 'RAF file version number' },
  1306. JPEGDigest => {
  1307. Notes => q{
  1308. an MD5 digest of the JPEG quantization tables is combined with the component
  1309. sub-sampling values to generate the value of this tag. The result is
  1310. compared to known values in an attempt to deduce the originating software
  1311. based only on the JPEG image data. For performance reasons, this tag is
  1312. generated only if specifically requested
  1313. },
  1314. },
  1315. JPEGQualityEstimate => {
  1316. Notes => q{
  1317. an estimate of the IJG JPEG quality setting for the image, calculated from
  1318. the quantization tables. For performance reasons, this tag is generated
  1319. only if specifically requested
  1320. },
  1321. },
  1322. Now => {
  1323. Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Time' },
  1324. Notes => q{
  1325. the current date/time. Useful when setting the tag values, eg.
  1326. C<"-modifydate<now">. Not generated unless specifically requested or the
  1327. RequestAll API option is set
  1328. },
  1329. PrintConv => '$self->ConvertDateTime($val)',
  1330. },
  1331. NewGUID => {
  1332. Groups => { 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'Other' },
  1333. Notes => q{
  1334. generates a new, random GUID with format
  1335. YYYYmmdd-HHMM-SSNN-PPPP-RRRRRRRRRRRR, where Y=year, m=month, d=day, H=hour,
  1336. M=minute, S=second, N=file sequence number in hex, P=process ID in hex, and
  1337. R=random hex number; without dashes with the -n option. Not generated
  1338. unless specifically requested or the RequestAll API option is set
  1339. },
  1340. PrintConv => '$val =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/; $val',
  1341. },
  1342. ID3Size => { Notes => 'size of the ID3 data block' },
  1343. Geotag => {
  1344. Writable => 1,
  1345. WriteOnly => 1,
  1346. AllowGroup => '(exif|gps|xmp|xmp-exif)',
  1347. Notes => q{
  1348. this write-only tag is used to define the GPS track log data or track log
  1349. file name. Currently supported track log formats are GPX, NMEA RMC/GGA/GLL,
  1350. KML, IGC, Garmin XML and TCX, Magellan PMGNTRK, Honeywell PTNTHPR, Winplus
  1351. Beacon text, and Bramor gEO log files. See L<geotag.html|../geotag.html>
  1352. for details
  1353. },
  1354. DelCheck => q{
  1355. require Image::ExifTool::Geotag;
  1356. # delete associated tags
  1357. Image::ExifTool::Geotag::SetGeoValues($self, undef, $wantGroup);
  1358. },
  1359. ValueConvInv => q{
  1360. require Image::ExifTool::Geotag;
  1361. # always warn because this tag is never set (warning is "\n" on success)
  1362. my $result = Image::ExifTool::Geotag::LoadTrackLog($self, $val);
  1363. return '' if not defined $result; # deleting geo tags
  1364. return $result if ref $result; # geotag data hash reference
  1365. warn "$result\n"; # error string
  1366. },
  1367. },
  1368. Geotime => {
  1369. Writable => 1,
  1370. WriteOnly => 1,
  1371. AllowGroup => '(exif|gps|xmp|xmp-exif)',
  1372. Notes => q{
  1373. this write-only tag is used to define a date/time for interpolating a
  1374. position in the GPS track specified by the Geotag tag. Writing this tag
  1375. causes GPS information to be written into the EXIF or XMP of the target
  1376. files. The local system timezone is assumed if the date/time value does not
  1377. contain a timezone. May be deleted to delete associated GPS tags. A group
  1378. name of 'EXIF' or 'XMP' may be specified to write or delete only EXIF or XMP
  1379. GPS tags. The Geotag tag must be assigned before this tag
  1380. },
  1381. DelCheck => q{
  1382. require Image::ExifTool::Geotag;
  1383. # delete associated tags
  1384. Image::ExifTool::Geotag::SetGeoValues($self, undef, $wantGroup);
  1385. },
  1386. ValueConvInv => q{
  1387. require Image::ExifTool::Geotag;
  1388. warn Image::ExifTool::Geotag::SetGeoValues($self, $val, $wantGroup) . "\n";
  1389. return undef;
  1390. },
  1391. },
  1392. Geosync => {
  1393. Writable => 1,
  1394. WriteOnly => 1,
  1395. AllowGroup => '(exif|gps|xmp|xmp-exif)',
  1396. Shift => 'Time', # enables "+=" syntax as well as "=+"
  1397. Notes => q{
  1398. this write-only tag specifies a time difference to add to Geotime for
  1399. synchronization with the GPS clock. For example, set this to "-12" if the
  1400. camera clock is 12 seconds faster than GPS time. Input format is
  1401. "[+-][[[DD ]HH:]MM:]SS[.ss]". Must be set before Geotime to be effective.
  1402. Additional features allow calculation of time differences and time drifts,
  1403. and extraction of synchronization times from image files. See the
  1404. L<geotagging documentation|../geotag.html> for details
  1405. },
  1406. ValueConvInv => q{
  1407. require Image::ExifTool::Geotag;
  1408. return Image::ExifTool::Geotag::ConvertGeosync($self, $val);
  1409. },
  1410. },
  1411. MDItemTags => {
  1412. Groups => { 1 => 'System', 2 => 'Other' },
  1413. Notes => q{
  1414. not a real tag. On OS X, a whole range of system-specific metadata tags
  1415. with names starting with "MDItem" may be extracted if specifically
  1416. requested, or if the MDItemTags API option is set. Requires that the "mdls"
  1417. utility is available
  1418. },
  1419. },
  1420. );
  1421. # YCbCrSubSampling values (used by JPEG SOF, EXIF and XMP)
  1422. %Image::ExifTool::JPEG::yCbCrSubSampling = (
  1423. '1 1' => 'YCbCr4:4:4 (1 1)', #PH
  1424. '2 1' => 'YCbCr4:2:2 (2 1)', #14 in Exif.pm
  1425. '2 2' => 'YCbCr4:2:0 (2 2)', #14 in Exif.pm
  1426. '4 1' => 'YCbCr4:1:1 (4 1)', #14 in Exif.pm
  1427. '4 2' => 'YCbCr4:1:0 (4 2)', #PH
  1428. '1 2' => 'YCbCr4:4:0 (1 2)', #PH
  1429. '1 4' => 'YCbCr4:4:1 (1 4)', #JD
  1430. '2 4' => 'YCbCr4:2:1 (2 4)', #JD
  1431. );
  1432. # define common JPEG segments here to avoid overhead of loading JPEG module
  1433. # JPEG SOF (start of frame) tags
  1434. # (ref http://www.w3.org/Graphics/JPEG/itu-t81.pdf)
  1435. %Image::ExifTool::JPEG::SOF = (
  1436. GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
  1437. NOTES => 'This information is extracted from the JPEG Start Of Frame segment.',
  1438. VARS => { NO_ID => 1 }, # tag ID's aren't meaningful for these tags
  1439. EncodingProcess => {
  1440. PrintHex => 1,
  1441. PrintConv => {
  1442. 0x0 => 'Baseline DCT, Huffman coding',
  1443. 0x1 => 'Extended sequential DCT, Huffman coding',
  1444. 0x2 => 'Progressive DCT, Huffman coding',
  1445. 0x3 => 'Lossless, Huffman coding',
  1446. 0x5 => 'Sequential DCT, differential Huffman coding',
  1447. 0x6 => 'Progressive DCT, differential Huffman coding',
  1448. 0x7 => 'Lossless, Differential Huffman coding',
  1449. 0x9 => 'Extended sequential DCT, arithmetic coding',
  1450. 0xa => 'Progressive DCT, arithmetic coding',
  1451. 0xb => 'Lossless, arithmetic coding',
  1452. 0xd => 'Sequential DCT, differential arithmetic coding',
  1453. 0xe => 'Progressive DCT, differential arithmetic coding',
  1454. 0xf => 'Lossless, differential arithmetic coding',
  1455. }
  1456. },
  1457. BitsPerSample => { },
  1458. ImageHeight => { },
  1459. ImageWidth => { },
  1460. ColorComponents => { },
  1461. YCbCrSubSampling => {
  1462. Notes => 'calculated from components table',
  1463. PrintConv => \%Image::ExifTool::JPEG::yCbCrSubSampling,
  1464. },
  1465. );
  1466. # JPEG JFIF APP0 definitions
  1467. %Image::ExifTool::JFIF::Main = (
  1468. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1469. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  1470. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  1471. GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
  1472. DATAMEMBER => [ 2, 3, 5 ],
  1473. 0 => {
  1474. Name => 'JFIFVersion',
  1475. Format => 'int8u[2]',
  1476. PrintConv => 'sprintf("%d.%.2d", split(" ",$val))',
  1477. Mandatory => 1,
  1478. },
  1479. 2 => {
  1480. Name => 'ResolutionUnit',
  1481. Writable => 1,
  1482. RawConv => '$$self{JFIFResolutionUnit} = $val',
  1483. PrintConv => {
  1484. 0 => 'None',
  1485. 1 => 'inches',
  1486. 2 => 'cm',
  1487. },
  1488. Priority => -1,
  1489. Mandatory => 1,
  1490. },
  1491. 3 => {
  1492. Name => 'XResolution',
  1493. Format => 'int16u',
  1494. Writable => 1,
  1495. Priority => -1,
  1496. RawConv => '$$self{JFIFXResolution} = $val',
  1497. Mandatory => 1,
  1498. },
  1499. 5 => {
  1500. Name => 'YResolution',
  1501. Format => 'int16u',
  1502. Writable => 1,
  1503. Priority => -1,
  1504. RawConv => '$$self{JFIFYResolution} = $val',
  1505. Mandatory => 1,
  1506. },
  1507. );
  1508. %Image::ExifTool::JFIF::Extension = (
  1509. GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
  1510. 0x10 => {
  1511. Name => 'ThumbnailImage',
  1512. Groups => { 2 => 'Preview' },
  1513. RawConv => '$self->ValidateImage(\$val,$tag)',
  1514. },
  1515. );
  1516. # Composite tags (accumulation of all Composite tag tables)
  1517. %Image::ExifTool::Composite = (
  1518. GROUPS => { 0 => 'Composite', 1 => 'Composite' },
  1519. TABLE_NAME => 'Image::ExifTool::Composite',
  1520. SHORT_NAME => 'Composite',
  1521. VARS => { NO_ID => 1 }, # want empty tagID's for Composite tags
  1522. WRITE_PROC => \&DummyWriteProc,
  1523. );
  1524. # static private ExifTool variables
  1525. %allTables = ( ); # list of all tables loaded (except Composite tags)
  1526. @tableOrder = ( ); # order the tables were loaded
  1527. #------------------------------------------------------------------------------
  1528. # Warning handler routines (warning string stored in $evalWarning)
  1529. #
  1530. # Set warning message
  1531. # Inputs: 0) warning string (undef to reset warning)
  1532. sub SetWarning($) { $evalWarning = $_[0]; }
  1533. # Get warning message
  1534. sub GetWarning() { return $evalWarning; }
  1535. # Clean unnecessary information (line number, LF) from warning
  1536. # Inputs: 0) warning string or undef to use current warning
  1537. # Returns: cleaned warning
  1538. sub CleanWarning(;$)
  1539. {
  1540. my $str = shift;
  1541. unless (defined $str) {
  1542. return undef unless defined $evalWarning;
  1543. $str = $evalWarning;
  1544. }
  1545. $str = $1 if $str =~ /(.*) at /s;
  1546. $str =~ s/\s+$//s;
  1547. return $str;
  1548. }
  1549. #==============================================================================
  1550. # New - create new ExifTool object
  1551. # Inputs: 0) reference to exiftool object or ExifTool class name
  1552. # Returns: blessed ExifTool object ref
  1553. sub new
  1554. {
  1555. local $_;
  1556. my $that = shift;
  1557. my $class = ref($that) || $that || 'Image::ExifTool';
  1558. my $self = bless {}, $class;
  1559. # make sure our main Exif tag table has been loaded
  1560. GetTagTable("Image::ExifTool::Exif::Main");
  1561. $self->ClearOptions(); # create default options hash
  1562. $$self{VALUE} = { }; # must initialize this for warning messages
  1563. $$self{DEL_GROUP} = { }; # lookup for groups to delete when writing
  1564. $$self{SAVE_COUNT} = 0; # count calls to SaveNewValues()
  1565. $$self{FILE_SEQUENCE} = 0; # sequence number for files when reading
  1566. # initialize our new groups for writing
  1567. $self->SetNewGroups(@defaultWriteGroups);
  1568. return $self;
  1569. }
  1570. #------------------------------------------------------------------------------
  1571. # ImageInfo - return specified information from image file
  1572. # Inputs: 0) [optional] ExifTool object reference
  1573. # 1) filename, file reference, or scalar data reference
  1574. # 2-N) list of tag names to find (or tag list reference or options reference)
  1575. # Returns: reference to hash of tag/value pairs (with "Error" entry on error)
  1576. # Notes:
  1577. # - if no tags names are specified, the values of all tags are returned
  1578. # - tags may be specified with leading '-' to exclude, or trailing '#' for ValueConv
  1579. # - can pass a reference to list of tags to find, in which case the list will
  1580. # be updated with the tags found in the proper case and in the specified order.
  1581. # - can pass reference to hash specifying options
  1582. # - returned tag values may be scalar references indicating binary data
  1583. # - see ClearOptions() below for a list of options and their default values
  1584. # Examples:
  1585. # use Image::ExifTool 'ImageInfo';
  1586. # my $info = ImageInfo($file, 'DateTimeOriginal', 'ImageSize');
  1587. # - or -
  1588. # my $et = new Image::ExifTool;
  1589. # my $info = $et->ImageInfo($file, \@tagList, {Sort=>'Group0'} );
  1590. sub ImageInfo($;@)
  1591. {
  1592. local $_;
  1593. # get our ExifTool object ($self) or create one if necessary
  1594. my $self;
  1595. if (ref $_[0] and UNIVERSAL::isa($_[0],'Image::ExifTool')) {
  1596. $self = shift;
  1597. } else {
  1598. $self = new Image::ExifTool;
  1599. }
  1600. my %saveOptions = %{$$self{OPTIONS}}; # save original options
  1601. # initialize file information
  1602. $$self{FILENAME} = $$self{RAF} = undef;
  1603. $self->ParseArguments(@_); # parse our function arguments
  1604. $self->ExtractInfo(undef); # extract meta information from image
  1605. my $info = $self->GetInfo(undef); # get requested information
  1606. $$self{OPTIONS} = \%saveOptions; # restore original options
  1607. return $info; # return requested information
  1608. }
  1609. #------------------------------------------------------------------------------
  1610. # Get/set ExifTool options
  1611. # Inputs: 0) ExifTool object reference,
  1612. # 1) Parameter name (case insensitive), 2) Value to set the option
  1613. # 3-N) More parameter/value pairs
  1614. # Returns: original value of last option specified
  1615. sub Options($$;@)
  1616. {
  1617. local $_;
  1618. my $self = shift;
  1619. my $options = $$self{OPTIONS};
  1620. my $oldVal;
  1621. while (@_) {
  1622. my $param = shift;
  1623. # fix parameter case if necessary
  1624. unless (exists $$options{$param}) {
  1625. my ($fixed) = grep /^$param$/i, keys %$options;
  1626. if ($fixed) {
  1627. $param = $fixed;
  1628. } else {
  1629. $param =~ s/^Group(\d*)$/Group$1/i;
  1630. }
  1631. }
  1632. $oldVal = $$options{$param};
  1633. last unless @_;
  1634. my $newVal = shift;
  1635. if ($param eq 'Lang') {
  1636. # allow this to be set to undef to select the default language
  1637. $newVal = $defaultLang unless defined $newVal;
  1638. if ($newVal eq $defaultLang) {
  1639. $$options{$param} = $newVal;
  1640. delete $$self{CUR_LANG};
  1641. # make sure the language is available
  1642. } elsif (eval "require Image::ExifTool::Lang::$newVal") {
  1643. my $xlat = "Image::ExifTool::Lang::${newVal}::Translate";
  1644. no strict 'refs';
  1645. if (%$xlat) {
  1646. $$self{CUR_LANG} = \%$xlat;
  1647. $$options{$param} = $newVal;
  1648. }
  1649. } # else don't change Lang
  1650. } elsif ($param eq 'Exclude' and defined $newVal) {
  1651. # clone Exclude list and expand shortcuts
  1652. my @exclude;
  1653. if (ref $newVal eq 'ARRAY') {
  1654. @exclude = @$newVal;
  1655. } else {
  1656. @exclude = ($newVal);
  1657. }
  1658. ExpandShortcuts(\@exclude, 1); # (also remove '#' suffix)
  1659. $$options{$param} = \@exclude;
  1660. } elsif ($param =~ /^Charset/ or $param eq 'IPTCCharset') {
  1661. # only allow valid character sets to be set
  1662. if ($newVal) {
  1663. my $charset = $charsetName{lc $newVal};
  1664. if ($charset) {
  1665. $$options{$param} = $charset;
  1666. # maintain backward-compatibility with old IPTCCharset option
  1667. $$options{CharsetIPTC} = $charset if $param eq 'IPTCCharset';
  1668. } else {
  1669. warn "Invalid Charset $newVal\n";
  1670. }
  1671. } elsif ($param eq 'CharsetEXIF' or $param eq 'CharsetFileName') {
  1672. $$options{$param} = $newVal; # only CharsetEXIF and CharsetFileName may be set to a false value
  1673. } elsif ($param eq 'CharsetQuickTime') {
  1674. $$options{$param} = 'MacRoman'; # QuickTime defaults to MacRoman
  1675. } else {
  1676. $$options{$param} = 'Latin'; # all others default to Latin
  1677. }
  1678. } elsif ($param eq 'UserParam') {
  1679. # allow initialization of entire UserParam hash
  1680. ref $newVal eq 'HASH' and $$options{$param} = { %$newVal }, next;
  1681. # set/reset single UserParam parameter
  1682. if ($newVal =~ /(.*?)=(.*)/s) {
  1683. $param = lc $1;
  1684. $newVal = $2;
  1685. } else {
  1686. $param = lc $newVal;
  1687. undef $newVal;
  1688. }
  1689. $oldVal = $$options{UserParam}{$param};
  1690. if (defined $newVal) {
  1691. if (length $newVal) {
  1692. $$options{UserParam}{$param} = $newVal;
  1693. } else {
  1694. delete $$options{UserParam}{$param};
  1695. }
  1696. }
  1697. } elsif ($param eq 'RequestTags') {
  1698. if (defined $newVal) {
  1699. # parse list from delimited string if necessary
  1700. my @reqList = (ref $newVal eq 'ARRAY') ? @$newVal : ($newVal =~ /(?:[-\w]+:)*[-\w?*]+/g);
  1701. ExpandShortcuts(\@reqList);
  1702. # add to existing list
  1703. $$options{$param} or $$options{$param} = [ ];
  1704. foreach (@reqList) {
  1705. push @{$$options{$param}}, lc($2) if /(^|:)([-\w?*]+)#?$/;
  1706. }
  1707. } else {
  1708. $$options{$param} = undef; # clear the list
  1709. }
  1710. } else {
  1711. if ($param eq 'Escape') {
  1712. # set ESCAPE_PROC
  1713. if (defined $newVal and $newVal eq 'XML') {
  1714. require Image::ExifTool::XMP;
  1715. $$self{ESCAPE_PROC} = \&Image::ExifTool::XMP::EscapeXML;
  1716. } elsif (defined $newVal and $newVal eq 'HTML') {
  1717. require Image::ExifTool::HTML;
  1718. $$self{ESCAPE_PROC} = \&Image::ExifTool::HTML::EscapeHTML;
  1719. } else {
  1720. delete $$self{ESCAPE_PROC};
  1721. }
  1722. # must forget saved values since they depend on Escape method
  1723. $$self{BOTH} = { };
  1724. }
  1725. $$options{$param} = $newVal;
  1726. }
  1727. }
  1728. return $oldVal;
  1729. }
  1730. #------------------------------------------------------------------------------
  1731. # ClearOptions - set options to default values
  1732. # Inputs: 0) ExifTool object reference
  1733. sub ClearOptions($)
  1734. {
  1735. local $_;
  1736. my $self = shift;
  1737. # create options hash with default values
  1738. # +-----------------------------------------------------+
  1739. # ! DON'T FORGET!! When adding any new option, must !
  1740. # ! decide how it is handled in SetNewValuesFromFile() !
  1741. # +-----------------------------------------------------+
  1742. # (Note: All options must exist in this lookup, even if undefined,
  1743. # to facilitate case-insensitive options. 'Group#' is handled specially)
  1744. $$self{OPTIONS} = {
  1745. Binary => undef, # flag to extract binary values even if tag not specified
  1746. ByteOrder => undef, # default byte order when creating EXIF information
  1747. Charset => 'UTF8', # character set for converting Unicode characters
  1748. CharsetEXIF => undef, # internal EXIF "ASCII" string encoding
  1749. CharsetFileName => undef, # external encoding for file names
  1750. CharsetID3 => 'Latin', # internal ID3v1 character set
  1751. CharsetIPTC => 'Latin', # fallback IPTC character set if no CodedCharacterSet
  1752. CharsetPhotoshop => 'Latin', # internal encoding for Photoshop resource names
  1753. CharsetQuickTime => 'MacRoman', # internal QuickTime string encoding
  1754. Compact => undef, # compact XMP and IPTC data
  1755. Composite => 1, # flag to calculate Composite tags
  1756. Compress => undef, # flag to write new values as compressed if possible
  1757. CoordFormat => undef, # GPS lat/long coordinate format
  1758. DateFormat => undef, # format for date/time
  1759. Duplicates => 1, # flag to save duplicate tag values
  1760. Escape => undef, # escape special characters
  1761. Exclude => undef, # tags to exclude
  1762. ExtendedXMP => 1, # strategy for reading extended XMP
  1763. ExtractEmbedded =>undef,# flag to extract information from embedded documents
  1764. FastScan => undef, # flag to avoid scanning for trailer
  1765. Filter => undef, # output filter for all tag values
  1766. FixBase => undef, # fix maker notes base offsets
  1767. GeoMaxIntSecs => 1800, # geotag maximum interpolation time (secs)
  1768. GeoMaxExtSecs => 1800, # geotag maximum extrapolation time (secs)
  1769. GeoMaxHDOP => undef, # geotag maximum HDOP
  1770. GeoMaxPDOP => undef, # geotag maximum PDOP
  1771. GeoMinSats => undef, # geotag minimum satellites
  1772. GlobalTimeShift => undef, # apply time shift to all extracted date/time values
  1773. # Group# => undef, # return tags for specified groups in family #
  1774. HtmlDump => 0, # HTML dump (0-3, higher # = bigger limit)
  1775. HtmlDumpBase => undef, # base address for HTML dump
  1776. IgnoreMinorErrors => undef, # ignore minor errors when reading/writing
  1777. Lang => $defaultLang,# localized language for descriptions etc
  1778. LargeFileSupport => undef, # flag indicating support of 64-bit file offsets
  1779. List => undef, # extract lists of PrintConv values into arrays
  1780. ListItem => undef, # used to return a specific item from lists
  1781. ListSep => ', ', # list item separator
  1782. ListSplit => undef, # regex for splitting list-type tag values when writing
  1783. MakerNotes => undef, # extract maker notes as a block
  1784. MDItemTags => undef, # extract OS X metadata item tags
  1785. MissingTagValue =>undef,# value for missing tags when expanded in expressions
  1786. NoPDFList => undef, # flag to avoid splitting PDF List-type tag values
  1787. Password => undef, # password for password-protected PDF documents
  1788. PNGEarlyXMP => undef, # write XMP in PNG images before IDAT chunk
  1789. PrintConv => 1, # flag to enable print conversion
  1790. QuickTimeUTC=> undef, # assume that QuickTime date/time tags are stored as UTC
  1791. RequestAll => undef, # extract all tags that must be specifically requested
  1792. RequestTags => undef, # extra tags to request (on top of those in the tag list)
  1793. SavePath => undef, # (undocumented) save family 5 location path
  1794. ScanForXMP => undef, # flag to scan for XMP information in all files
  1795. Sort => 'Input', # order to sort found tags (Input, File, Tag, Descr, Group#)
  1796. Sort2 => 'File', # secondary sort order for tags in a group (File, Tag, Descr)
  1797. StrictDate => undef, # flag to return undef for invalid date conversions
  1798. Struct => undef, # return structures as hash references
  1799. SystemTags => undef, # extract additional File System tags
  1800. TextOut => \*STDOUT,# file for Verbose/HtmlDump output
  1801. Unknown => 0, # flag to get values of unknown tags (0-2)
  1802. UserParam => { }, # user parameters for InsertTagValues()
  1803. Verbose => 0, # print verbose messages (0-5, higher # = more verbose)
  1804. WriteMode => 'wcg', # enable all write modes by default
  1805. XMPAutoConv => 1, # automatic conversion of unknown XMP tag values
  1806. };
  1807. # keep necessary member variables in sync with options
  1808. delete $$self{CUR_LANG};
  1809. delete $$self{ESCAPE_PROC};
  1810. # load user-defined default options
  1811. if (%Image::ExifTool::UserDefined::Options) {
  1812. foreach (keys %Image::ExifTool::UserDefined::Options) {
  1813. $self->Options($_, $Image::ExifTool::UserDefined::Options{$_});
  1814. }
  1815. }
  1816. }
  1817. #------------------------------------------------------------------------------
  1818. # Extract meta information from image
  1819. # Inputs: 0) ExifTool object reference
  1820. # 1-N) Same as ImageInfo()
  1821. # Returns: 1 if this was a valid image, 0 otherwise
  1822. # Notes: pass an undefined value to avoid parsing arguments
  1823. # Internal 'ReEntry' option allows this routine to be called recursively
  1824. sub ExtractInfo($;@)
  1825. {
  1826. local $_;
  1827. my $self = shift;
  1828. my $options = $$self{OPTIONS}; # pointer to current options
  1829. my $fast = $$options{FastScan};
  1830. my (%saveOptions, $reEntry, $rsize, $type, @startTime);
  1831. # check for internal ReEntry option to allow recursive calls to ExtractInfo
  1832. if (ref $_[1] eq 'HASH' and $_[1]{ReEntry} and
  1833. (ref $_[0] eq 'SCALAR' or ref $_[0] eq 'GLOB'))
  1834. {
  1835. # save necessary members for restoring later
  1836. $reEntry = {
  1837. RAF => $$self{RAF},
  1838. PROCESSED => $$self{PROCESSED},
  1839. EXIF_DATA => $$self{EXIF_DATA},
  1840. EXIF_POS => $$self{EXIF_POS},
  1841. FILE_TYPE => $$self{FILE_TYPE},
  1842. };
  1843. $$self{RAF} = new File::RandomAccess($_[0]);
  1844. $$self{PROCESSED} = { };
  1845. delete $$self{EXIF_DATA};
  1846. delete $$self{EXIF_POS};
  1847. } else {
  1848. if (defined $_[0] or $$options{HtmlDump}) {
  1849. %saveOptions = %$options; # save original options
  1850. # require duplicates for html dump
  1851. $self->Options(Duplicates => 1) if $$options{HtmlDump};
  1852. if (defined $_[0]) {
  1853. # only initialize filename if called with arguments
  1854. $$self{FILENAME} = undef; # name of file (or '' if we didn't open it)
  1855. $$self{RAF} = undef; # RandomAccess object reference
  1856. $self->ParseArguments(@_); # initialize from our arguments
  1857. }
  1858. }
  1859. # initialize ExifTool object members
  1860. $self->Init();
  1861. delete $$self{MAKER_NOTE_FIXUP}; # fixup information for extracted maker notes
  1862. delete $$self{MAKER_NOTE_BYTE_ORDER};
  1863. # return our version number
  1864. my $reqAll = $$options{RequestAll};
  1865. $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE");
  1866. $self->FoundTag('Now', TimeNow()) if $$self{REQ_TAG_LOOKUP}{now} or $reqAll;
  1867. $self->FoundTag('NewGUID', NewGUID()) if $$self{REQ_TAG_LOOKUP}{newguid} or $reqAll;
  1868. # generate sequence number if necessary
  1869. if ($$self{REQ_TAG_LOOKUP}{filesequence} or $reqAll) {
  1870. $self->FoundTag('FileSequence', $$self{FILE_SEQUENCE});
  1871. }
  1872. if ($$self{REQ_TAG_LOOKUP}{processingtime} or $reqAll) {
  1873. eval { require Time::HiRes; @startTime = Time::HiRes::gettimeofday() };
  1874. if (not @startTime and $$self{REQ_TAG_LOOKUP}{processingtime}) {
  1875. $self->WarnOnce('Install Time::HiRes to generate ProcessingTime');
  1876. }
  1877. }
  1878. ++$$self{FILE_SEQUENCE}; # count files read
  1879. }
  1880. my $filename = $$self{FILENAME}; # image file name ('' if already open)
  1881. my $raf = $$self{RAF}; # RandomAccess object
  1882. local *EXIFTOOL_FILE; # avoid clashes with global namespace
  1883. my $realname = $filename;
  1884. unless ($raf) {
  1885. # save file name
  1886. if (defined $filename and $filename ne '') {
  1887. unless ($filename eq '-') {
  1888. # extract file name from pipe if necessary
  1889. $realname =~ /\|$/ and $realname =~ s/^.*?"(.*?)".*/$1/s;
  1890. my ($dir, $name);
  1891. if (eval { require File::Basename }) {
  1892. $dir = File::Basename::dirname($realname);
  1893. $name = File::Basename::basename($realname);
  1894. } else {
  1895. ($name = $realname) =~ tr/\\/\//;
  1896. # remove path
  1897. $dir = length($1) ? $1 : '/' if $name =~ s/(.*)\///;
  1898. }
  1899. $self->FoundTag('FileName', $name);
  1900. $self->FoundTag('Directory', $dir) if defined $dir and length $dir;
  1901. if ($$self{REQ_TAG_LOOKUP}{filepath} or
  1902. ($$options{RequestAll} and not $$self{EXCL_TAG_LOOKUP}{filepath}))
  1903. {
  1904. local $SIG{'__WARN__'} = \&SetWarning;
  1905. if (eval { require Cwd }) {
  1906. my $path = eval { Cwd::abs_path($filename) };
  1907. $self->FoundTag('FilePath', $path) if defined $path;
  1908. } elsif ($$self{REQ_TAG_LOOKUP}{filepath}) {
  1909. $self->WarnOnce('The Perl Cwd module must be installed to use FilePath');
  1910. }
  1911. }
  1912. # get size of resource fork on Mac OS
  1913. $rsize = -s "$filename/..namedfork/rsrc" if $^O eq 'darwin' and not $$self{IN_RESOURCE};
  1914. }
  1915. # open the file
  1916. if ($self->Open(\*EXIFTOOL_FILE, $filename)) {
  1917. # create random access file object
  1918. $raf = new File::RandomAccess(\*EXIFTOOL_FILE);
  1919. # patch to force pipe to be buffered because seek returns success
  1920. # in Windows cmd shell pipe even though it really failed
  1921. $$raf{TESTED} = -1 if $filename eq '-' or $filename =~ /\|$/;
  1922. $$self{RAF} = $raf;
  1923. } else {
  1924. $self->Error('Error opening file');
  1925. }
  1926. } else {
  1927. $self->Error('No file specified');
  1928. }
  1929. }
  1930. if ($raf) {
  1931. my @stat;
  1932. if ($reEntry) {
  1933. # we already set these tags
  1934. } elsif (not $$raf{FILE_PT}) {
  1935. # get file size from image in memory
  1936. $self->FoundTag('FileSize', length ${$$raf{BUFF_PT}});
  1937. } elsif (-f $$raf{FILE_PT}) {
  1938. # get file tags if this is a plain file
  1939. my $fileSize = -s _;
  1940. @stat = stat _;
  1941. my ($aTime, $mTime, $cTime) = $self->GetFileTime($$raf{FILE_PT});
  1942. $self->FoundTag('FileSize', $fileSize) if defined $fileSize;
  1943. $self->FoundTag('ResourceForkSize', $rsize) if $rsize;
  1944. $self->FoundTag('FileModifyDate', $mTime) if defined $mTime;
  1945. $self->FoundTag('FileAccessDate', $aTime) if defined $aTime;
  1946. my $cTag = $^O eq 'MSWin32' ? 'FileCreateDate' : 'FileInodeChangeDate';
  1947. $self->FoundTag($cTag, $cTime) if defined $cTime;
  1948. $self->FoundTag('FilePermissions', $stat[2]) if defined $stat[2];
  1949. } else {
  1950. @stat = stat $$raf{FILE_PT};
  1951. }
  1952. # extract more system info if SystemTags option is set
  1953. if (@stat) {
  1954. my $sys = $$options{SystemTags} || ($$options{RequestAll} and not defined $$options{SystemTags});
  1955. my $req = $$self{REQ_TAG_LOOKUP};
  1956. if ($sys or $$req{fileattributes}) {
  1957. my @attr = ($stat[2] & 0xf000, $stat[2] & 0x0e00);
  1958. # add Windows file attributes if available
  1959. if ($^O eq 'MSWin32' and defined $filename and $filename ne '' and $filename ne '-') {
  1960. local $SIG{'__WARN__'} = \&SetWarning;
  1961. if (eval { require Win32API::File }) {
  1962. my $wattr;
  1963. my $file = $filename;
  1964. if ($self->EncodeFileName($file)) {
  1965. $wattr = eval { Win32API::File::GetFileAttributesW($file) };
  1966. } else {
  1967. $wattr = eval { Win32API::File::GetFileAttributes($file) };
  1968. }
  1969. push @attr, $wattr if defined $wattr and $wattr != 0xffffffff;
  1970. }
  1971. }
  1972. $self->FoundTag('FileAttributes', "@attr");
  1973. }
  1974. $self->FoundTag('FileDeviceNumber', $stat[0]) if $sys or $$req{filedevicenumber};
  1975. $self->FoundTag('FileInodeNumber', $stat[1]) if $sys or $$req{fileinodenumber};
  1976. $self->FoundTag('FileHardLinks', $stat[3]) if $sys or $$req{filehardlinks};
  1977. $self->FoundTag('FileUserID', $stat[4]) if $sys or $$req{fileuserid};
  1978. $self->FoundTag('FileGroupID', $stat[5]) if $sys or $$req{filegroupid};
  1979. $self->FoundTag('FileDeviceID', $stat[6]) if $sys or $$req{filedeviceid};
  1980. $self->FoundTag('FileBlockSize', $stat[11]) if $sys or $$req{fileblocksize};
  1981. $self->FoundTag('FileBlockCount', $stat[12]) if $sys or $$req{fileblockcount};
  1982. }
  1983. # extract MDItem tags if requested (not extracted with RequestAll for performance reasons)
  1984. if ($^O eq 'darwin' and ($$options{MDItemTags} || grep /^mditem/i, keys %{$$self{REQ_TAG_LOOKUP}}) and
  1985. defined $filename and $filename ne '' and $filename ne '-')
  1986. {
  1987. my ($fn, $tag, $val);
  1988. local $SIG{'__WARN__'} = \&SetWarning;
  1989. undef $evalWarning;
  1990. ($fn = $filename) =~ s/([`"\$\\])/\\$1/g; # escape necessary characters
  1991. my @mdls = eval {`mdls "$fn" 2> /dev/null`}; # get OS X metadata
  1992. unless ($evalWarning) {
  1993. my $extra = GetTagTable('Image::ExifTool::Extra');
  1994. foreach (@mdls) {
  1995. chomp;
  1996. if (ref $val ne 'ARRAY') {
  1997. s/^k(MDItem\w+)\s*= // or next;
  1998. $tag = $1;
  1999. $_ eq '(' and $val = [ ], next; # (start of a list)
  2000. $_ = '' if $_ eq '(null)';
  2001. s/^"// and s/"$//; # remove quotes if they exist
  2002. $val = $_;
  2003. } elsif ($_ eq ')') { # (end of a list)
  2004. $_ = $$val[0];
  2005. next unless defined $_;
  2006. } else {
  2007. # add item to list
  2008. s/^ //; # remove leading spaces
  2009. s/,$//; # remove trailing comma
  2010. $_ = '' if $_ eq '(null)';
  2011. s/^"// and s/"$//; # remove quotes if they exist
  2012. push @$val, $_;
  2013. next;
  2014. }
  2015. # add to Extra tags if not done already
  2016. unless ($$extra{$tag}) {
  2017. # check for a date/time format
  2018. my %tagInfo = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/ ? (
  2019. Groups => { 1 => 'System', 2 => 'Time' },
  2020. ValueConv => '$val=~tr/-/:/; $val=~s/ ([-+])/$1/; $val',
  2021. PrintConv => '$self->ConvertDateTime($val)',
  2022. ) : ( Groups => { 1 => 'System', 2 => 'Other' } );
  2023. $tagInfo{Name} = $tag;
  2024. $tagInfo{List} = 1 if ref $val eq 'ARRAY';
  2025. $tagInfo{Groups}{2} = 'Audio' if $tag =~ /Audio/;
  2026. $tagInfo{Groups}{2} = 'Author' if $tag =~ /(Copyright|Author)/;
  2027. AddTagToTable($extra, $tag, \%tagInfo);
  2028. }
  2029. $self->FoundTag($tag, $val);
  2030. undef $val;
  2031. }
  2032. }
  2033. }
  2034. # get list of file types to check
  2035. my ($tiffType, %noMagic, $recognizedExt);
  2036. my $ext = $$self{FILE_EXT} = GetFileExtension($realname);
  2037. # set $recognizedExt if this file type is recognized by extension only
  2038. $recognizedExt = $ext if defined $ext and not defined $magicNumber{$ext} and
  2039. defined $moduleName{$ext} and not $moduleName{$ext};
  2040. my @fileTypeList = GetFileType($realname);
  2041. if (@fileTypeList) {
  2042. # add remaining types to end of list so we test them all
  2043. my $pat = join '|', @fileTypeList;
  2044. push @fileTypeList, grep(!/^($pat)$/, @fileTypes);
  2045. $tiffType = $$self{FILE_EXT};
  2046. unless ($fast and $fast == 3) {
  2047. $noMagic{MXF} = 1; # don't do magic number test on MXF or DV files
  2048. $noMagic{DV} = 1;
  2049. }
  2050. } else {
  2051. # scan through all recognized file types
  2052. @fileTypeList = @fileTypes;
  2053. $tiffType = 'TIFF';
  2054. }
  2055. push @fileTypeList, ''; # end of list marker
  2056. # initialize the input file for seeking in binary data
  2057. $raf->BinMode(); # set binary mode before we start reading
  2058. my $pos = $raf->Tell(); # get file position so we can rewind
  2059. my %dirInfo = ( RAF => $raf, Base => $pos );
  2060. # loop through list of file types to test
  2061. my ($buff, $seekErr);
  2062. # read first 1024 bytes of file for testing
  2063. $raf->Read($buff, 1024) or $buff = '';
  2064. $raf->Seek($pos, 0) or $seekErr = 1;
  2065. until ($seekErr) {
  2066. $type = shift @fileTypeList;
  2067. if ($type) {
  2068. # do quick test for this file type to avoid loading module unnecessarily
  2069. next if $magicNumber{$type} and $buff !~ /^$magicNumber{$type}/s and
  2070. not $noMagic{$type};
  2071. next if $weakMagic{$type} and defined $recognizedExt;
  2072. } elsif (not defined $type) {
  2073. last;
  2074. } elsif ($recognizedExt) {
  2075. $type = $recognizedExt; # set type from recognized file extension only
  2076. } else {
  2077. # last ditch effort to scan past unknown header for JPEG/TIFF
  2078. next unless $buff =~ /(\xff\xd8\xff|MM\0\x2a|II\x2a\0)/g;
  2079. $type = ($1 eq "\xff\xd8\xff") ? 'JPEG' : 'TIFF';
  2080. my $skip = pos($buff) - length($1);
  2081. $dirInfo{Base} = $pos + $skip;
  2082. $raf->Seek($pos + $skip, 0) or $seekErr = 1, last;
  2083. $self->Warn("Skipped unknown $skip byte header");
  2084. }
  2085. # save file type in member variable
  2086. $$self{FILE_TYPE} = $type;
  2087. $dirInfo{Parent} = ($type eq 'TIFF') ? $tiffType : $type;
  2088. # don't process the file when FastScan == 3
  2089. if ($fast and $fast == 3 and not $processType{$type}) {
  2090. unless ($weakMagic{$type} and (not $ext or $ext ne $type)) {
  2091. $self->SetFileType($dirInfo{Parent});
  2092. }
  2093. last;
  2094. }
  2095. my $module = $moduleName{$type};
  2096. $module = $type unless defined $module;
  2097. my $func = "Process$type";
  2098. # load module if necessary
  2099. if ($module) {
  2100. require "Image/ExifTool/$module.pm";
  2101. $func = "Image::ExifTool::${module}::$func";
  2102. } elsif ($module eq '0') {
  2103. $self->SetFileType();
  2104. $self->Warn('Unsupported file type');
  2105. last;
  2106. }
  2107. push @{$$self{PATH}}, $type; # save file type in metadata PATH
  2108. # process the file
  2109. no strict 'refs';
  2110. my $result = &$func($self, \%dirInfo);
  2111. use strict 'refs';
  2112. pop @{$$self{PATH}};
  2113. last if $result; # all done if successful
  2114. # seek back to try again from the same position in the file
  2115. $raf->Seek($pos, 0) or $seekErr = 1, last;
  2116. }
  2117. if ($seekErr) {
  2118. $self->Error('Error seeking in file');
  2119. } elsif ($self->Options('ScanForXMP') and (not defined $type or
  2120. (not $fast and not $$self{FoundXMP})))
  2121. {
  2122. # scan for XMP
  2123. $raf->Seek($pos, 0);
  2124. require Image::ExifTool::XMP;
  2125. Image::ExifTool::XMP::ScanForXMP($self, $raf) and $type = '';
  2126. }
  2127. if (not defined $type and not $$self{DOC_NUM}) {
  2128. # if we were given a single image with a known type there
  2129. # must be a format error since we couldn't read it, otherwise
  2130. # it is likely we don't support images of this type
  2131. my $fileType = GetFileType($realname);
  2132. my $err;
  2133. if (not $fileType) {
  2134. $err = 'Unknown file type';
  2135. } elsif ($fileType eq 'RAW') {
  2136. $err = 'Unsupported RAW file type';
  2137. } else {
  2138. $err = 'File format error';
  2139. }
  2140. $self->Error($err);
  2141. }
  2142. # extract binary EXIF data block only if requested
  2143. if (defined $$self{EXIF_DATA} and length $$self{EXIF_DATA} > 16 and
  2144. ($$self{REQ_TAG_LOOKUP}{exif} or
  2145. # (not extracted normally, so check TAGS_FROM_FILE)
  2146. ($$self{TAGS_FROM_FILE} and not $$self{EXCL_TAG_LOOKUP}{exif})))
  2147. {
  2148. $self->FoundTag('EXIF', $$self{EXIF_DATA});
  2149. }
  2150. unless ($reEntry) {
  2151. $$self{PATH} = [ ]; # reset PATH
  2152. # calculate Composite tags
  2153. $self->BuildCompositeTags() if $$options{Composite};
  2154. # do our HTML dump if requested
  2155. if ($$self{HTML_DUMP}) {
  2156. $raf->Seek(0, 2); # seek to end of file
  2157. $$self{HTML_DUMP}->FinishTiffDump($self, $raf->Tell());
  2158. my $pos = $$options{HtmlDumpBase};
  2159. $pos = ($$self{FIRST_EXIF_POS} || 0) unless defined $pos;
  2160. my $dataPt = defined $$self{EXIF_DATA} ? \$$self{EXIF_DATA} : undef;
  2161. undef $dataPt if defined $$self{EXIF_POS} and $pos != $$self{EXIF_POS};
  2162. my $success = $$self{HTML_DUMP}->Print($raf, $dataPt, $pos,
  2163. $$options{TextOut}, $$options{HtmlDump},
  2164. $$self{FILENAME} ? "HTML Dump ($$self{FILENAME})" : 'HTML Dump');
  2165. $self->Warn("Error reading $$self{HTML_DUMP}{ERROR}") if $success < 0;
  2166. }
  2167. }
  2168. if ($filename) {
  2169. $raf->Close(); # close the file if we opened it
  2170. # process the resource fork as an embedded file on Mac filesystems
  2171. if ($rsize and $$options{ExtractEmbedded}) {
  2172. local *RESOURCE_FILE;
  2173. if ($self->Open(\*RESOURCE_FILE, "$filename/..namedfork/rsrc")) {
  2174. $$self{DOC_NUM} = $$self{DOC_COUNT} + 1;
  2175. $$self{IN_RESOURCE} = 1;
  2176. $self->ExtractInfo(\*RESOURCE_FILE, { ReEntry => 1 });
  2177. close RESOURCE_FILE;
  2178. delete $$self{IN_RESOURCE};
  2179. } else {
  2180. $self->Warn('Error opening resource fork');
  2181. }
  2182. }
  2183. }
  2184. }
  2185. @startTime and $self->FoundTag('ProcessingTime', Time::HiRes::tv_interval(\@startTime));
  2186. # restore original options
  2187. %saveOptions and $$self{OPTIONS} = \%saveOptions;
  2188. if ($reEntry) {
  2189. # restore necessary members when exiting re-entrant code
  2190. $$self{$_} = $$reEntry{$_} foreach keys %$reEntry;
  2191. }
  2192. # ($type may be undef without an Error when processing sub-documents)
  2193. return 0 if not defined $type or exists $$self{VALUE}{Error};
  2194. return 1;
  2195. }
  2196. #------------------------------------------------------------------------------
  2197. # Get hash of extracted meta information
  2198. # Inputs: 0) ExifTool object reference
  2199. # 1-N) options hash reference, tag list reference or tag names
  2200. # Returns: Reference to information hash
  2201. # Notes: - pass an undefined value to avoid parsing arguments
  2202. # - If groups are specified, first groups take precedence if duplicate
  2203. # tags found but Duplicates option not set.
  2204. # - tag names may end in '#' to extract ValueConv value
  2205. sub GetInfo($;@)
  2206. {
  2207. local $_;
  2208. my $self = shift;
  2209. my %saveOptions;
  2210. unless (@_ and not defined $_[0]) {
  2211. %saveOptions = %{$$self{OPTIONS}}; # save original options
  2212. # must set FILENAME so it isn't parsed from the arguments
  2213. $$self{FILENAME} = '' unless defined $$self{FILENAME};
  2214. $self->ParseArguments(@_);
  2215. }
  2216. # get reference to list of tags for which we will return info
  2217. my ($rtnTags, $byValue, $wildTags) = $self->SetFoundTags();
  2218. # build hash of tag information
  2219. my (%info, %ignored);
  2220. my $conv = $$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
  2221. foreach (@$rtnTags) {
  2222. my $val = $self->GetValue($_, $conv);
  2223. defined $val or $ignored{$_} = 1, next;
  2224. $info{$_} = $val;
  2225. }
  2226. # override specified tags with ValueConv value if necessary
  2227. if (@$byValue) {
  2228. # first determine the number of times each non-ValueConv value is used
  2229. my %nonVal;
  2230. $nonVal{$_} = ($nonVal{$_} || 0) + 1 foreach @$rtnTags;
  2231. --$nonVal{$$rtnTags[$_]} foreach @$byValue;
  2232. # loop through ValueConv tags, updating tag keys and returned values
  2233. foreach (@$byValue) {
  2234. my $tag = $$rtnTags[$_];
  2235. my $val = $self->GetValue($tag, 'ValueConv');
  2236. next unless defined $val;
  2237. my $vtag = $tag;
  2238. # generate a new tag key like "Tag #" or "Tag #(1)"
  2239. $vtag =~ s/( |$)/ #/;
  2240. unless (defined $$self{VALUE}{$vtag}) {
  2241. $$self{VALUE}{$vtag} = $$self{VALUE}{$tag};
  2242. $$self{TAG_INFO}{$vtag} = $$self{TAG_INFO}{$tag};
  2243. $$self{TAG_EXTRA}{$vtag} = $$self{TAG_EXTRA}{$tag};
  2244. $$self{FILE_ORDER}{$vtag} = $$self{FILE_ORDER}{$tag};
  2245. # remove existing PrintConv entry unless we are using it too
  2246. delete $info{$tag} unless $nonVal{$tag};
  2247. }
  2248. $$rtnTags[$_] = $vtag; # store ValueConv value with new tag key
  2249. $info{$vtag} = $val; # return ValueConv value
  2250. }
  2251. }
  2252. # remove ignored tags from the list
  2253. my $reqTags = $$self{REQUESTED_TAGS} || [ ];
  2254. if (%ignored) {
  2255. if (not @$reqTags) {
  2256. my @goodTags;
  2257. foreach (@$rtnTags) {
  2258. push @goodTags, $_ unless $ignored{$_};
  2259. }
  2260. $rtnTags = $$self{FOUND_TAGS} = \@goodTags;
  2261. } elsif (@$wildTags) {
  2262. # only remove tags specified by wildcard
  2263. my @goodTags;
  2264. my $i = 0;
  2265. foreach (@$rtnTags) {
  2266. if (@$wildTags and $i == $$wildTags[0]) {
  2267. shift @$wildTags;
  2268. push @goodTags, $_ unless $ignored{$_};
  2269. } else {
  2270. push @goodTags, $_;
  2271. }
  2272. ++$i;
  2273. }
  2274. $rtnTags = $$self{FOUND_TAGS} = \@goodTags;
  2275. }
  2276. }
  2277. # return sorted tag list if provided with a list reference
  2278. if ($$self{IO_TAG_LIST}) {
  2279. # use file order by default if no tags specified
  2280. # (no such thing as 'Input' order in this case)
  2281. my $sort = $$self{OPTIONS}{Sort};
  2282. $sort = 'File' unless @$reqTags or ($sort and $sort ne 'Input');
  2283. # return tags in specified sort order
  2284. @{$$self{IO_TAG_LIST}} = $self->GetTagList($rtnTags, $sort, $$self{OPTIONS}{Sort2});
  2285. }
  2286. # restore original options
  2287. %saveOptions and $$self{OPTIONS} = \%saveOptions;
  2288. return \%info;
  2289. }
  2290. #------------------------------------------------------------------------------
  2291. # Combine information from a list of info hashes
  2292. # Unless Duplicates is enabled, first entry found takes priority
  2293. # Inputs: 0) ExifTool object reference, 1-N) list of info hash references
  2294. # Returns: Combined information hash reference
  2295. sub CombineInfo($;@)
  2296. {
  2297. local $_;
  2298. my $self = shift;
  2299. my (%combinedInfo, $info, $tag, %haveInfo);
  2300. if ($$self{OPTIONS}{Duplicates}) {
  2301. while ($info = shift) {
  2302. foreach $tag (keys %$info) {
  2303. $combinedInfo{$tag} = $$info{$tag};
  2304. }
  2305. }
  2306. } else {
  2307. while ($info = shift) {
  2308. foreach $tag (keys %$info) {
  2309. my $tagName = GetTagName($tag);
  2310. next if $haveInfo{$tagName};
  2311. $haveInfo{$tagName} = 1;
  2312. $combinedInfo{$tag} = $$info{$tag};
  2313. }
  2314. }
  2315. }
  2316. return \%combinedInfo;
  2317. }
  2318. #------------------------------------------------------------------------------
  2319. # Inputs: 0) ExifTool object reference
  2320. # 1) [optional] reference to info hash or tag list ref (default is found tags)
  2321. # 2) [optional] sort order ('File', 'Input', ...)
  2322. # 3) [optional] secondary sort order
  2323. # Returns: List of tags in specified order
  2324. sub GetTagList($;$$$)
  2325. {
  2326. local $_;
  2327. my ($self, $info, $sort, $sort2) = @_;
  2328. my $foundTags;
  2329. if (ref $info eq 'HASH') {
  2330. my @tags = keys %$info;
  2331. $foundTags = \@tags;
  2332. } elsif (ref $info eq 'ARRAY') {
  2333. $foundTags = $info;
  2334. }
  2335. my $fileOrder = $$self{FILE_ORDER};
  2336. if ($foundTags) {
  2337. # make sure a FILE_ORDER entry exists for all tags
  2338. # (note: already generated bogus entries for FOUND_TAGS case below)
  2339. foreach (@$foundTags) {
  2340. next if defined $$fileOrder{$_};
  2341. $$fileOrder{$_} = 999;
  2342. }
  2343. } else {
  2344. $sort = $info if $info and not $sort;
  2345. $foundTags = $$self{FOUND_TAGS} || $self->SetFoundTags() or return undef;
  2346. }
  2347. $sort or $sort = $$self{OPTIONS}{Sort};
  2348. # return original list if no sort order specified
  2349. return @$foundTags unless $sort and $sort ne 'Input';
  2350. if ($sort eq 'Tag' or $sort eq 'Alpha') {
  2351. return sort @$foundTags;
  2352. } elsif ($sort =~ /^Group(\d*(:\d+)*)/) {
  2353. my $family = $1 || 0;
  2354. # want to maintain a basic file order with the groups
  2355. # ordered in the way they appear in the file
  2356. my (%groupCount, %groupOrder);
  2357. my $numGroups = 0;
  2358. my $tag;
  2359. foreach $tag (sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags) {
  2360. my $group = $self->GetGroup($tag, $family);
  2361. my $num = $groupCount{$group};
  2362. $num or $num = $groupCount{$group} = ++$numGroups;
  2363. $groupOrder{$tag} = $num;
  2364. }
  2365. $sort2 or $sort2 = $$self{OPTIONS}{Sort2};
  2366. if ($sort2) {
  2367. if ($sort2 eq 'Tag' or $sort2 eq 'Alpha') {
  2368. return sort { $groupOrder{$a} <=> $groupOrder{$b} or $a cmp $b } @$foundTags;
  2369. } elsif ($sort2 eq 'Descr') {
  2370. my $desc = $self->GetDescriptions($foundTags);
  2371. return sort { $groupOrder{$a} <=> $groupOrder{$b} or
  2372. $$desc{$a} cmp $$desc{$b} } @$foundTags;
  2373. }
  2374. }
  2375. return sort { $groupOrder{$a} <=> $groupOrder{$b} or
  2376. $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
  2377. } elsif ($sort eq 'Descr') {
  2378. my $desc = $self->GetDescriptions($foundTags);
  2379. return sort { $$desc{$a} cmp $$desc{$b} } @$foundTags;
  2380. } else {
  2381. return sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
  2382. }
  2383. }
  2384. #------------------------------------------------------------------------------
  2385. # Get list of found tags in specified sort order
  2386. # Inputs: 0) ExifTool object reference, 1) sort order ('File', 'Input', ...)
  2387. # 2) secondary sort order
  2388. # Returns: List of tag keys in specified order
  2389. # Notes: If not specified, sort order is taken from OPTIONS
  2390. sub GetFoundTags($;$$)
  2391. {
  2392. local $_;
  2393. my ($self, $sort, $sort2) = @_;
  2394. my $foundTags = $$self{FOUND_TAGS} || $self->SetFoundTags() or return undef;
  2395. return $self->GetTagList($foundTags, $sort, $sort2);
  2396. }
  2397. #------------------------------------------------------------------------------
  2398. # Get list of requested tags
  2399. # Inputs: 0) ExifTool object reference
  2400. # Returns: List of requested tag keys
  2401. sub GetRequestedTags($)
  2402. {
  2403. local $_;
  2404. return @{$_[0]{REQUESTED_TAGS}};
  2405. }
  2406. #------------------------------------------------------------------------------
  2407. # Get tag value
  2408. # Inputs: 0) ExifTool object reference
  2409. # 1) tag key (or flattened tagInfo for getting field values, not part of public API)
  2410. # 2) [optional] Value type: PrintConv, ValueConv, Both, Raw or Rational, the default
  2411. # is PrintConv or ValueConv, depending on the PrintConv option setting
  2412. # 3) raw field value (not part of public API)
  2413. # Returns: Scalar context: tag value or undefined
  2414. # List context: list of values or empty list
  2415. sub GetValue($$;$)
  2416. {
  2417. local $_;
  2418. my ($self, $tag, $type) = @_; # plus: ($fieldValue)
  2419. my (@convTypes, $tagInfo, $valueConv, $both);
  2420. # figure out what conversions to do
  2421. if ($type) {
  2422. return $$self{RATIONAL}{$tag} if $type eq 'Rational';
  2423. } else {
  2424. $type = $$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv';
  2425. }
  2426. # start with the raw value
  2427. my $value = $$self{VALUE}{$tag};
  2428. if (not defined $value) {
  2429. return wantarray ? () : undef unless ref $tag;
  2430. # get the value of a structure field
  2431. $tagInfo = $tag;
  2432. $tag = $$tagInfo{Name};
  2433. $value = $_[3];
  2434. # (note: type "Both" is not allowed for structure fields)
  2435. if ($type ne 'Raw') {
  2436. push @convTypes, 'ValueConv';
  2437. push @convTypes, 'PrintConv' unless $type eq 'ValueConv';
  2438. }
  2439. } else {
  2440. $tagInfo = $$self{TAG_INFO}{$tag};
  2441. if ($$tagInfo{Struct} and ref $value) {
  2442. # must load XMPStruct.pl just in case (should already be loaded if
  2443. # a structure was extracted, but we could also arrive here if a simple
  2444. # list of values was stored incorrectly in a Struct tag)
  2445. require 'Image/ExifTool/XMPStruct.pl';
  2446. # convert strucure field values
  2447. unless ($type eq 'Both') {
  2448. # (note: ConvertStruct handles the filtering and escaping too if necessary)
  2449. return Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,$type);
  2450. }
  2451. $valueConv = Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,'ValueConv');
  2452. $value = Image::ExifTool::XMP::ConvertStruct($self,$tagInfo,$value,'PrintConv');
  2453. # (must not save these in $$self{BOTH} because the values may have been escaped)
  2454. return ($valueConv, $value);
  2455. }
  2456. if ($type ne 'Raw') {
  2457. # use values we calculated already if we stored them
  2458. $both = $$self{BOTH}{$tag};
  2459. if ($both) {
  2460. if ($type eq 'PrintConv') {
  2461. $value = $$both[1];
  2462. } elsif ($type eq 'ValueConv') {
  2463. $value = $$both[0];
  2464. $value = $$both[1] unless defined $value;
  2465. } else {
  2466. ($valueConv, $value) = @$both;
  2467. }
  2468. } else {
  2469. push @convTypes, 'ValueConv';
  2470. push @convTypes, 'PrintConv' unless $type eq 'ValueConv';
  2471. }
  2472. }
  2473. }
  2474. # do the conversions
  2475. my (@val, @prt, @raw, $convType);
  2476. foreach $convType (@convTypes) {
  2477. # don't convert a scalar reference or structure
  2478. last if ref $value eq 'SCALAR' and not $$tagInfo{ConvertBinary};
  2479. my $conv = $$tagInfo{$convType};
  2480. unless (defined $conv) {
  2481. if ($convType eq 'ValueConv') {
  2482. next unless $$tagInfo{Binary};
  2483. $conv = '\$val'; # return scalar reference for binary values
  2484. } else {
  2485. # use PRINT_CONV from tag table if PrintConv doesn't exist
  2486. next unless defined($conv = $$tagInfo{Table}{PRINT_CONV});
  2487. next if exists $$tagInfo{$convType};
  2488. }
  2489. }
  2490. # save old ValueConv value if we want Both
  2491. $valueConv = $value if $type eq 'Both' and $convType eq 'PrintConv';
  2492. my ($i, $val, $vals, @values, $convList);
  2493. # split into list if conversion is an array
  2494. if (ref $conv eq 'ARRAY') {
  2495. $convList = $conv;
  2496. $conv = $$convList[0];
  2497. my @valList = (ref $value eq 'ARRAY') ? @$value : split ' ', $value;
  2498. # reorganize list if specified (Note: The writer currently doesn't
  2499. # relist values, so they may be grouped but the order must not change)
  2500. my $relist = $$tagInfo{Relist};
  2501. if ($relist) {
  2502. my (@newList, $oldIndex);
  2503. foreach $oldIndex (@$relist) {
  2504. my ($newVal, @join);
  2505. if (ref $oldIndex) {
  2506. foreach (@$oldIndex) {
  2507. push @join, $valList[$_] if defined $valList[$_];
  2508. }
  2509. $newVal = join(' ', @join) if @join;
  2510. } else {
  2511. $newVal = $valList[$oldIndex];
  2512. }
  2513. push @newList, $newVal if defined $newVal;
  2514. }
  2515. $value = \@newList;
  2516. } else {
  2517. $value = \@valList;
  2518. }
  2519. return wantarray ? () : undef unless @$value;
  2520. }
  2521. # initialize array so we can iterate over values in list
  2522. if (ref $value eq 'ARRAY') {
  2523. if (defined $$tagInfo{RawJoin}) {
  2524. $val = join ' ', @$value;
  2525. } else {
  2526. $i = 0;
  2527. $vals = $value;
  2528. $val = $$vals[0];
  2529. }
  2530. } else {
  2531. $val = $value;
  2532. }
  2533. # loop through all values in list
  2534. for (;;) {
  2535. if (defined $conv) {
  2536. # get values of required tags if this is a Composite tag
  2537. if (ref $val eq 'HASH' and not @val) {
  2538. # disable escape of source values so we don't double escape them
  2539. my $oldEscape = $$self{ESCAPE_PROC};
  2540. delete $$self{ESCAPE_PROC};
  2541. # temporarily delete filter so it isn't applied to the Require'd values
  2542. my $oldFilter = $$self{OPTIONS}{Filter};
  2543. delete $$self{OPTIONS}{Filter};
  2544. foreach (keys %$val) {
  2545. $raw[$_] = $$self{VALUE}{$$val{$_}};
  2546. ($val[$_], $prt[$_]) = $self->GetValue($$val{$_}, 'Both');
  2547. next if defined $val[$_] or not $$tagInfo{Require}{$_};
  2548. $$self{ESCAPE_PROC} = $oldEscape;
  2549. return wantarray ? () : undef;
  2550. }
  2551. $$self{OPTIONS}{Filter} = $oldFilter if defined $oldFilter;
  2552. $$self{ESCAPE_PROC} = $oldEscape;
  2553. # set $val to $val[0], or \@val for a CODE ref conversion
  2554. $val = ref $conv eq 'CODE' ? \@val : $val[0];
  2555. }
  2556. if (ref $conv eq 'HASH') {
  2557. # look up converted value in hash
  2558. my $lc;
  2559. if (defined($value = $$conv{$val})) {
  2560. # override with our localized language PrintConv if available
  2561. if ($$self{CUR_LANG} and $convType eq 'PrintConv' and
  2562. # (no need to check for lang-alt tag names -- they won't have a PrintConv)
  2563. ref($lc = $$self{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and
  2564. ($lc = $$lc{PrintConv}) and ($lc = $$lc{$value}))
  2565. {
  2566. $value = $self->Decode($lc, 'UTF8');
  2567. }
  2568. } else {
  2569. if ($$conv{BITMASK}) {
  2570. $value = DecodeBits($val, $$conv{BITMASK}, $$tagInfo{BitsPerWord});
  2571. # override with localized language strings
  2572. if (defined $value and $$self{CUR_LANG} and $convType eq 'PrintConv' and
  2573. ref($lc = $$self{CUR_LANG}{$$tagInfo{Name}}) eq 'HASH' and
  2574. ($lc = $$lc{PrintConv}))
  2575. {
  2576. my @vals = split ', ', $value;
  2577. foreach (@vals) {
  2578. $_ = $$lc{$_} if defined $$lc{$_};
  2579. }
  2580. $value = join ', ', @vals;
  2581. }
  2582. } elsif (not $$conv{OTHER} or
  2583. # use alternate conversion routine if available
  2584. not defined($value = &{$$conv{OTHER}}($val, undef, $conv)))
  2585. {
  2586. if (($$tagInfo{PrintHex} or
  2587. ($$tagInfo{Mask} and not defined $$tagInfo{PrintHex}))
  2588. and $val and IsInt($val) and $convType eq 'PrintConv')
  2589. {
  2590. $val = sprintf('0x%x',$val);
  2591. }
  2592. $value = "Unknown ($val)";
  2593. }
  2594. }
  2595. } else {
  2596. # call subroutine or do eval to convert value
  2597. local $SIG{'__WARN__'} = \&SetWarning;
  2598. undef $evalWarning;
  2599. if (ref $conv eq 'CODE') {
  2600. $value = &$conv($val, $self);
  2601. } else {
  2602. #### eval ValueConv/PrintConv ($val, $self, @val, @prt, @raw)
  2603. $value = eval $conv;
  2604. $@ and $evalWarning = $@;
  2605. }
  2606. $self->Warn("$convType $tag: " . CleanWarning()) if $evalWarning;
  2607. }
  2608. } else {
  2609. $value = $val;
  2610. }
  2611. last unless $vals;
  2612. # must store a separate copy of each binary data value in the list
  2613. if (ref $value eq 'SCALAR') {
  2614. my $tval = $$value;
  2615. $value = \$tval;
  2616. }
  2617. # save this converted value and step to next value in list
  2618. push @values, $value if defined $value;
  2619. if (++$i >= scalar(@$vals)) {
  2620. $value = \@values if @values;
  2621. last;
  2622. }
  2623. $val = $$vals[$i];
  2624. $conv = $$convList[$i] if $convList;
  2625. }
  2626. # return undefined now if no value
  2627. return wantarray ? () : undef unless defined $value;
  2628. # join back into single value if split for conversion list
  2629. if ($convList and ref $value eq 'ARRAY') {
  2630. $value = join($convType eq 'PrintConv' ? '; ' : ' ', @$value);
  2631. }
  2632. }
  2633. if ($type eq 'Both') {
  2634. # save both (unescaped) values because we often need them again
  2635. # (Composite tags need "Both" and often Require one tag for various Composite tags)
  2636. $$self{BOTH}{$tag} = [ $valueConv, $value ] unless $both;
  2637. # escape values if necessary
  2638. if ($$self{ESCAPE_PROC}) {
  2639. DoEscape($value, $$self{ESCAPE_PROC});
  2640. if (defined $valueConv) {
  2641. DoEscape($valueConv, $$self{ESCAPE_PROC});
  2642. } else {
  2643. $valueConv = $value;
  2644. }
  2645. } elsif (not defined $valueConv) {
  2646. # $valueConv is undefined if there was no print conversion done
  2647. $valueConv = $value;
  2648. }
  2649. $self->Filter(\$value) if defined $$self{OPTIONS}{Filter};
  2650. # return Both values as a list (ValueConv, PrintConv)
  2651. return ($valueConv, $value);
  2652. }
  2653. # escape value if necessary
  2654. DoEscape($value, $$self{ESCAPE_PROC}) if $$self{ESCAPE_PROC};
  2655. # filter if necessary
  2656. $self->Filter(\$value) if defined $$self{OPTIONS}{Filter} and $type eq 'PrintConv';
  2657. if (ref $value eq 'ARRAY') {
  2658. if (defined $$self{OPTIONS}{ListItem}) {
  2659. $value = $$value[$$self{OPTIONS}{ListItem}];
  2660. } elsif (wantarray) {
  2661. # return array if requested
  2662. return @$value;
  2663. } elsif ($type eq 'PrintConv' and not $$self{OPTIONS}{List} and not ref $$value[0]) {
  2664. # join PrintConv values in comma-separated string if List option not used
  2665. # and list contains simple scalars (otherwise return ARRAY ref)
  2666. $value = join $$self{OPTIONS}{ListSep}, @$value;
  2667. }
  2668. }
  2669. return $value;
  2670. }
  2671. #------------------------------------------------------------------------------
  2672. # Get tag identification number
  2673. # Inputs: 0) ExifTool object reference, 1) tag key
  2674. # Returns: Scalar context: Tag ID if available, otherwise ''
  2675. # List context: 0) Tag ID (or ''), 1) language code (or undef)
  2676. sub GetTagID($$)
  2677. {
  2678. my ($self, $tag) = @_;
  2679. my $tagInfo = $$self{TAG_INFO}{$tag};
  2680. return '' unless $tagInfo and defined $$tagInfo{TagID};
  2681. return ($$tagInfo{TagID}, $$tagInfo{LangCode}) if wantarray;
  2682. return $$tagInfo{TagID};
  2683. }
  2684. #------------------------------------------------------------------------------
  2685. # Get tag table name
  2686. # Inputs: 0) ExifTool object reference, 1) tag key
  2687. # Returns: Table name if available, otherwise ''
  2688. sub GetTableName($$)
  2689. {
  2690. my ($self, $tag) = @_;
  2691. my $tagInfo = $$self{TAG_INFO}{$tag} or return '';
  2692. return $$tagInfo{Table}{SHORT_NAME};
  2693. }
  2694. #------------------------------------------------------------------------------
  2695. # Get tag index number
  2696. # Inputs: 0) ExifTool object reference, 1) tag key
  2697. # Returns: Table index number, or undefined if this tag isn't indexed
  2698. sub GetTagIndex($$)
  2699. {
  2700. my ($self, $tag) = @_;
  2701. my $tagInfo = $$self{TAG_INFO}{$tag} or return undef;
  2702. return $$tagInfo{Index};
  2703. }
  2704. #------------------------------------------------------------------------------
  2705. # Get description for specified tag
  2706. # Inputs: 0) ExifTool object reference, 1) tag key
  2707. # Returns: Tag description
  2708. # Notes: Will always return a defined value, even if description isn't available
  2709. sub GetDescription($$)
  2710. {
  2711. local $_;
  2712. my ($self, $tag) = @_;
  2713. my ($desc, $name);
  2714. my $tagInfo = $$self{TAG_INFO}{$tag};
  2715. # ($tagInfo won't be defined for missing tags extracted with -f)
  2716. if ($tagInfo) {
  2717. # use alternate language description if available
  2718. while ($$self{CUR_LANG}) {
  2719. $desc = $$self{CUR_LANG}{$$tagInfo{Name}};
  2720. if ($desc) {
  2721. # must look up Description if this tag also has a PrintConv
  2722. $desc = $$desc{Description} or last if ref $desc;
  2723. } else {
  2724. # look up default language of lang-alt tag
  2725. last unless $$tagInfo{LangCode} and
  2726. ($name = $$tagInfo{Name}) =~ s/-$$tagInfo{LangCode}$// and
  2727. $desc = $$self{CUR_LANG}{$name};
  2728. $desc = $$desc{Description} or last if ref $desc;
  2729. $desc .= " ($$tagInfo{LangCode})";
  2730. }
  2731. # escape description if necessary
  2732. DoEscape($desc, $$self{ESCAPE_PROC}) if $$self{ESCAPE_PROC};
  2733. # return description in proper Charset
  2734. return $self->Decode($desc, 'UTF8');
  2735. }
  2736. $desc = $$tagInfo{Description};
  2737. }
  2738. # just make the tag more readable if description doesn't exist
  2739. unless ($desc) {
  2740. $desc = MakeDescription(GetTagName($tag));
  2741. # save description in tag information
  2742. $$tagInfo{Description} = $desc if $tagInfo;
  2743. }
  2744. return $desc;
  2745. }
  2746. #------------------------------------------------------------------------------
  2747. # Get group name for specified tag
  2748. # Inputs: 0) ExifTool object reference
  2749. # 1) tag key (or reference to tagInfo hash, not part of the public API)
  2750. # 2) [optional] group family (-1 to get extended group list, or multiple
  2751. # families separated by colons to return multiple groups as a string)
  2752. # Returns: Scalar context: Group name (for family 0 if not otherwise specified)
  2753. # Array context: Group name if family specified, otherwise list of
  2754. # group names for each family. Returns '' for undefined tag.
  2755. # Notes: Mutiple families may be specified with ':' in family argument (eg. '1:2')
  2756. sub GetGroup($$;$)
  2757. {
  2758. local $_;
  2759. my ($self, $tag, $family) = @_;
  2760. my ($tagInfo, @groups, @families, $simplify, $byTagInfo, $ex);
  2761. if (ref $tag eq 'HASH') {
  2762. $tagInfo = $tag;
  2763. $tag = $$tagInfo{Name};
  2764. # set flag so we don't get extra information for an extracted tag
  2765. $byTagInfo = 1;
  2766. } else {
  2767. $tagInfo = $$self{TAG_INFO}{$tag} || { };
  2768. $ex = $$self{TAG_EXTRA}{$tag};
  2769. }
  2770. my $groups = $$tagInfo{Groups};
  2771. # fill in default groups unless already done
  2772. # (after this, Groups 0-2 in tagInfo are guaranteed to be defined)
  2773. unless ($$tagInfo{GotGroups}) {
  2774. my $tagTablePtr = $$tagInfo{Table} || { GROUPS => { } };
  2775. # construct our group list
  2776. $groups or $groups = $$tagInfo{Groups} = { };
  2777. # fill in default groups
  2778. foreach (0..2) {
  2779. $$groups{$_} = $$tagTablePtr{GROUPS}{$_} || '' unless $$groups{$_};
  2780. }
  2781. # set flag indicating group list was built
  2782. $$tagInfo{GotGroups} = 1;
  2783. }
  2784. if (defined $family and $family ne '-1') {
  2785. if ($family =~ /[^\d]/) {
  2786. @families = ($family =~ /\d+/g);
  2787. return(($ex && $$ex{G0}) || $$groups{0}) unless @families;
  2788. $simplify = 1 unless $family =~ /^:/;
  2789. undef $family;
  2790. foreach (0..2) { $groups[$_] = $$groups{$_}; }
  2791. } else {
  2792. return(($ex && $$ex{"G$family"}) || $$groups{$family}) if $family == 0 or $family == 2;
  2793. $groups[1] = $$groups{1};
  2794. }
  2795. } else {
  2796. return(($ex && $$ex{G0}) || $$groups{0}) unless wantarray;
  2797. foreach (0..2) { $groups[$_] = $$groups{$_}; }
  2798. }
  2799. $groups[3] = 'Main';
  2800. $groups[4] = ($tag =~ /\((\d+)\)$/) ? "Copy$1" : '';
  2801. # handle dynamic group names if necessary
  2802. if ($ex and not $byTagInfo) {
  2803. $groups[0] = $$ex{G0} if $$ex{G0};
  2804. $groups[1] = $$ex{G1} =~ /^\+(.*)/ ? "$groups[1]$1" : $$ex{G1} if $$ex{G1};
  2805. $groups[3] = 'Doc' . $$ex{G3} if $$ex{G3};
  2806. $groups[5] = $$ex{G5} || $groups[1] if defined $$ex{G5};
  2807. }
  2808. if ($family) {
  2809. return $groups[$family] || '' if $family > 0;
  2810. # add additional matching group names to list
  2811. # eg) for MIE-Doc, also add MIE1, MIE1-Doc, MIE-Doc1 and MIE1-Doc1
  2812. # and for MIE2-Doc3, also add MIE2, MIE-Doc3, MIE2-Doc and MIE-Doc
  2813. if ($groups[1] =~ /^MIE(\d*)-(.+?)(\d*)$/) {
  2814. push @groups, 'MIE' . ($1 || '1');
  2815. push @groups, 'MIE' . ($1 ? '' : '1') . "-$2$3";
  2816. push @groups, "MIE$1-$2" . ($3 ? '' : '1');
  2817. push @groups, 'MIE' . ($1 ? '' : '1') . "-$2" . ($3 ? '' : '1');
  2818. }
  2819. }
  2820. if (@families) {
  2821. my @grps;
  2822. # create list of group names (without identical adjacent groups if simplifying)
  2823. foreach (@families) {
  2824. my $grp = $groups[$_] or next;
  2825. push @grps, $grp unless $simplify and @grps and $grp eq $grps[-1];
  2826. }
  2827. # remove leading "Main:" if simplifying
  2828. shift @grps if $simplify and @grps > 1 and $grps[0] eq 'Main';
  2829. # return colon-separated string of group names
  2830. return join ':', @grps;
  2831. }
  2832. return @groups;
  2833. }
  2834. #------------------------------------------------------------------------------
  2835. # Get group names for specified tags
  2836. # Inputs: 0) ExifTool object reference
  2837. # 1) [optional] information hash reference (default all extracted info)
  2838. # 2) [optional] group family (default 0)
  2839. # Returns: List of group names in alphabetical order
  2840. sub GetGroups($;$$)
  2841. {
  2842. local $_;
  2843. my $self = shift;
  2844. my $info = shift;
  2845. my $family;
  2846. # figure out our arguments
  2847. if (ref $info ne 'HASH') {
  2848. $family = $info;
  2849. $info = $$self{VALUE};
  2850. } else {
  2851. $family = shift;
  2852. }
  2853. $family = 0 unless defined $family;
  2854. # get a list of all groups in specified information
  2855. my ($tag, %groups);
  2856. foreach $tag (keys %$info) {
  2857. $groups{ $self->GetGroup($tag, $family) } = 1;
  2858. }
  2859. return sort keys %groups;
  2860. }
  2861. #------------------------------------------------------------------------------
  2862. # Set priority for group where new values are written
  2863. # Inputs: 0) ExifTool object reference,
  2864. # 1-N) group names (reset to default if no groups specified)
  2865. sub SetNewGroups($;@)
  2866. {
  2867. local $_;
  2868. my ($self, @groups) = @_;
  2869. @groups or @groups = @defaultWriteGroups;
  2870. my $count = @groups;
  2871. my %priority;
  2872. foreach (@groups) {
  2873. $priority{lc($_)} = $count--;
  2874. }
  2875. $priority{file} = 10; # 'File' group is always written (Comment)
  2876. $priority{composite} = 10; # 'Composite' group is always written
  2877. # set write priority (higher # is higher priority)
  2878. $$self{WRITE_PRIORITY} = \%priority;
  2879. $$self{WRITE_GROUPS} = \@groups;
  2880. }
  2881. #------------------------------------------------------------------------------
  2882. # Build Composite tags from Require'd/Desire'd tags
  2883. # Inputs: 0) ExifTool object reference
  2884. # Note: Tag values are calculated in alphabetical order unless a tag Require's
  2885. # or Desire's another Composite tag, in which case the calculation is
  2886. # deferred until after the other tag is calculated.
  2887. sub BuildCompositeTags($)
  2888. {
  2889. local $_;
  2890. my $self = shift;
  2891. $$self{BuildingComposite} = 1;
  2892. # first, add user-defined Composite tags if necessary
  2893. if (%UserDefined and $UserDefined{'Image::ExifTool::Composite'}) {
  2894. AddCompositeTags($UserDefined{'Image::ExifTool::Composite'}, 1);
  2895. delete $UserDefined{'Image::ExifTool::Composite'};
  2896. }
  2897. my @tagList = sort keys %Image::ExifTool::Composite;
  2898. my %tagsUsed;
  2899. my $rawValue = $$self{VALUE};
  2900. for (;;) {
  2901. my %notBuilt;
  2902. $notBuilt{$_} = 1 foreach @tagList;
  2903. my @deferredTags;
  2904. my $tag;
  2905. COMPOSITE_TAG:
  2906. foreach $tag (@tagList) {
  2907. next if $specialTags{$tag};
  2908. my $tagInfo = $self->GetTagInfo(\%Image::ExifTool::Composite, $tag);
  2909. next unless $tagInfo;
  2910. # put required tags into array and make sure they all exist
  2911. my $subDoc = ($$tagInfo{SubDoc} and $$self{DOC_COUNT});
  2912. my $require = $$tagInfo{Require} || { };
  2913. my $desire = $$tagInfo{Desire} || { };
  2914. my $inhibit = $$tagInfo{Inhibit} || { };
  2915. # loop through sub-documents if necessary
  2916. my $doc;
  2917. for (;;) {
  2918. my (%tagKey, $found, $index);
  2919. # save Require'd and Desire'd tag values in list
  2920. for ($index=0; ; ++$index) {
  2921. my $reqTag = $$require{$index} || $$desire{$index} || $$inhibit{$index};
  2922. unless ($reqTag) {
  2923. # allow Composite with no Require'd or Desire'd tags
  2924. $found = 1 if $index == 0;
  2925. last;
  2926. }
  2927. # add family 3 group if generating Composite tags for sub-documents
  2928. # (unless tag already begins with family 3 group name)
  2929. if ($subDoc and $reqTag !~ /^(Main|Doc\d+):/) {
  2930. $reqTag = ($doc ? "Doc$doc:" : 'Main:') . $reqTag;
  2931. }
  2932. # allow tag group to be specified
  2933. if ($reqTag =~ /^(.*):(.+)/) {
  2934. my ($reqGroup, $name) = ($1, $2);
  2935. if ($reqGroup eq 'Composite' and $notBuilt{$name}) {
  2936. push @deferredTags, $tag;
  2937. next COMPOSITE_TAG;
  2938. }
  2939. # (CAREFUL! keys may not be sequential if one was deleted)
  2940. my ($i, @keys);
  2941. my $key = $name;
  2942. my $last = ($$self{DUPL_TAG}{$name} || 0);
  2943. for ($i=0;;) {
  2944. push @keys, $key if defined $$rawValue{$key};
  2945. last if ++$i > $last;
  2946. $key = "$name ($i)";
  2947. }
  2948. # find first matching tag
  2949. $key = $self->GroupMatches($reqGroup, \@keys);
  2950. $reqTag = $key if $key;
  2951. } elsif ($notBuilt{$reqTag}) {
  2952. # calculate this tag later if it relies on another
  2953. # Composite tag which hasn't been calculated yet
  2954. push @deferredTags, $tag;
  2955. next COMPOSITE_TAG;
  2956. }
  2957. if (defined $$rawValue{$reqTag}) {
  2958. if ($$inhibit{$index}) {
  2959. $found = 0;
  2960. last;
  2961. } else {
  2962. $found = 1;
  2963. }
  2964. } elsif ($$require{$index}) {
  2965. $found = 0;
  2966. last; # don't continue since we require this tag
  2967. }
  2968. $tagKey{$index} = $reqTag;
  2969. }
  2970. if ($doc) {
  2971. if ($found) {
  2972. $$self{DOC_NUM} = $doc;
  2973. $self->FoundTag($tagInfo, \%tagKey);
  2974. delete $$self{DOC_NUM};
  2975. }
  2976. next if ++$doc <= $$self{DOC_COUNT};
  2977. last;
  2978. } elsif ($found) {
  2979. delete $notBuilt{$tag}; # this tag is OK to build now
  2980. # keep track of all Require'd tag keys
  2981. foreach (keys %tagKey) {
  2982. # only tag keys with same name as a Composite tag
  2983. # can be replaced (also eliminates keys with
  2984. # instance numbers which can't be replaced either)
  2985. next unless $Image::ExifTool::Composite{$tagKey{$_}};
  2986. my $keyRef = \$tagKey{$_};
  2987. $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ];
  2988. push @{$tagsUsed{$$keyRef}}, $keyRef;
  2989. }
  2990. # save reference to tag key lookup as value for Composite tag
  2991. my $key = $self->FoundTag($tagInfo, \%tagKey);
  2992. # check to see if we just replaced one of the tag keys we Require'd
  2993. if (defined $key and $tagsUsed{$key}) {
  2994. foreach (@{$tagsUsed{$key}}) {
  2995. $$_ = $$self{MOVED_KEY}; # replace with new tag key
  2996. }
  2997. delete $tagsUsed{$key}; # can't be replaced again
  2998. }
  2999. } elsif (not defined $found) {
  3000. delete $notBuilt{$tag}; # tag can't be built anyway
  3001. }
  3002. last unless $subDoc;
  3003. $doc = 1; # continue to process the 1st sub-document
  3004. }
  3005. }
  3006. last unless @deferredTags;
  3007. if (@deferredTags == @tagList) {
  3008. # everything was deferred in the last pass,
  3009. # must be a circular dependency
  3010. warn "Circular dependency in Composite tags\n";
  3011. last;
  3012. }
  3013. @tagList = @deferredTags; # calculate deferred tags now
  3014. }
  3015. delete $$self{BuildingComposite};
  3016. }
  3017. #------------------------------------------------------------------------------
  3018. # Get tag name (removes copy index)
  3019. # Inputs: 0) Tag key
  3020. # Returns: Tag name
  3021. sub GetTagName($)
  3022. {
  3023. local $_;
  3024. $_[0] =~ /^(\S+)/;
  3025. return $1;
  3026. }
  3027. #------------------------------------------------------------------------------
  3028. # Get list of shortcuts
  3029. # Returns: Shortcut list (sorted alphabetically)
  3030. sub GetShortcuts()
  3031. {
  3032. local $_;
  3033. require Image::ExifTool::Shortcuts;
  3034. return sort keys %Image::ExifTool::Shortcuts::Main;
  3035. }
  3036. #------------------------------------------------------------------------------
  3037. # Get file type for specified extension
  3038. # Inputs: 0) file name or extension (case is not significant),
  3039. # or FileType value if a description is requested
  3040. # 1) flag to return long description instead of type ('0' to return any recognized type)
  3041. # Returns: File type (or desc) or undef if extension not supported or if
  3042. # description is the same as the input FileType. In array context,
  3043. # may return more than one file type if the file may be different formats.
  3044. # Returns list of all supported extensions if no file specified
  3045. sub GetFileType(;$$)
  3046. {
  3047. local $_;
  3048. my ($file, $desc) = @_;
  3049. unless (defined $file) {
  3050. my @types;
  3051. if (defined $desc and $desc eq '0') {
  3052. # return all recognized types
  3053. @types = sort keys %fileTypeLookup;
  3054. } else {
  3055. # return all supported types
  3056. foreach (sort keys %fileTypeLookup) {
  3057. my $module = $moduleName{$_};
  3058. $module = $moduleName{$fileTypeLookup{$_}} unless defined $module;
  3059. push @types, $_ unless defined $module and $module eq '0';
  3060. }
  3061. }
  3062. return @types;
  3063. }
  3064. my $fileType;
  3065. my $fileExt = GetFileExtension($file);
  3066. $fileExt = uc($file) unless $fileExt;
  3067. $fileExt and $fileType = $fileTypeLookup{$fileExt}; # look up the file type
  3068. $fileType = $fileTypeLookup{$fileType} if $fileType and not ref $fileType;
  3069. # return description if specified
  3070. # (allow input $file to be a FileType for this purpose)
  3071. if ($desc) {
  3072. return $fileType ? $$fileType[1] : $fileDescription{$file};
  3073. } elsif ($fileType and (not defined $desc or $desc ne '0')) {
  3074. # return only supported file types
  3075. my $mod = $moduleName{$$fileType[0]};
  3076. undef $fileType if defined $mod and $mod eq '0';
  3077. }
  3078. $fileType or return wantarray ? () : undef;
  3079. $fileType = $$fileType[0]; # get file type (or list of types)
  3080. if (wantarray) {
  3081. return @$fileType if ref $fileType eq 'ARRAY';
  3082. } elsif ($fileType) {
  3083. $fileType = $fileExt if ref $fileType eq 'ARRAY';
  3084. }
  3085. return $fileType;
  3086. }
  3087. #------------------------------------------------------------------------------
  3088. # Return true if we can write the specified file type
  3089. # Inputs: 0) file name or ext
  3090. # Returns: true if writable, 0 if not writable, undef if unrecognized
  3091. sub CanWrite($)
  3092. {
  3093. local $_;
  3094. my $file = shift or return undef;
  3095. my ($type) = GetFileType($file) or return undef;
  3096. if ($noWriteFile{$type}) {
  3097. # can't write TIFF files with certain extensions (various RAW formats)
  3098. my $ext = GetFileExtension($file) || uc($file);
  3099. return grep(/^$ext$/, @{$noWriteFile{$type}}) ? 0 : 1 if $ext;
  3100. }
  3101. unless (%writeTypes) {
  3102. $writeTypes{$_} = 1 foreach @writeTypes;
  3103. }
  3104. return $writeTypes{$type};
  3105. }
  3106. #------------------------------------------------------------------------------
  3107. # Return true if we can create the specified file type
  3108. # Inputs: 0) file name or ext
  3109. # Returns: true if creatable, 0 if not writable, undef if unrecognized
  3110. sub CanCreate($)
  3111. {
  3112. local $_;
  3113. my $file = shift or return undef;
  3114. my $ext = GetFileExtension($file) || uc($file);
  3115. my $type = GetFileType($file) or return undef;
  3116. return 1 if $createTypes{$ext} or $createTypes{$type};
  3117. return 0;
  3118. }
  3119. #==============================================================================
  3120. # Functions below this are not part of the public API
  3121. # Initialize member variables for reading or writing a new file
  3122. # Inputs: 0) ExifTool object reference
  3123. sub Init($)
  3124. {
  3125. local $_;
  3126. my $self = shift;
  3127. # delete all DataMember variables (lower-case names)
  3128. foreach (keys %$self) {
  3129. /[a-z]/ and delete $$self{$_};
  3130. }
  3131. delete $$self{FOUND_TAGS}; # list of found tags
  3132. delete $$self{EXIF_DATA}; # the EXIF data block
  3133. delete $$self{EXIF_POS}; # EXIF position in file
  3134. delete $$self{FIRST_EXIF_POS}; # position of first EXIF in file
  3135. delete $$self{HTML_DUMP}; # html dump information
  3136. delete $$self{SET_GROUP0}; # group0 name override
  3137. delete $$self{SET_GROUP1}; # group1 name override
  3138. delete $$self{DOC_NUM}; # current embedded document number
  3139. $$self{DOC_COUNT} = 0; # count of embedded documents processed
  3140. $$self{BASE} = 0; # base for offsets from start of file
  3141. $$self{FILE_ORDER} = { }; # * hash of tag order in file ('*' = based on tag key)
  3142. $$self{VALUE} = { }; # * hash of raw tag values
  3143. $$self{BOTH} = { }; # * hash for Value/PrintConv values of Require'd tags
  3144. $$self{RATIONAL} = { }; # * hash of original rational components
  3145. $$self{TAG_INFO} = { }; # * hash of tag information
  3146. $$self{TAG_EXTRA} = { }; # * hash of extra tag information (dynamic group names)
  3147. $$self{PRIORITY} = { }; # * priority of current tags
  3148. $$self{LIST_TAGS} = { }; # hash of tagInfo refs for active List-type tags
  3149. $$self{PROCESSED} = { }; # hash of processed directory start positions
  3150. $$self{DIR_COUNT} = { }; # count various types of directories
  3151. $$self{DUPL_TAG} = { }; # last-used index for duplicate-tag keys
  3152. $$self{WARNED_ONCE}= { }; # WarnOnce() warnings already issued
  3153. $$self{WRITTEN} = { }; # list of tags written (selected tags only)
  3154. $$self{PATH} = [ ]; # current subdirectory path in file when reading
  3155. $$self{NUM_FOUND} = 0; # total number of tags found (incl. duplicates)
  3156. $$self{CHANGED} = 0; # number of tags changed (writer only)
  3157. $$self{INDENT} = ' '; # initial indent for verbose messages
  3158. $$self{PRIORITY_DIR} = ''; # the priority directory name
  3159. $$self{LOW_PRIORITY_DIR} = { PreviewIFD => 1 }; # names of priority 0 directories
  3160. $$self{TIFF_TYPE} = ''; # type of TIFF data (APP1, TIFF, NEF, etc...)
  3161. $$self{Make} = ''; # camera make
  3162. $$self{Model} = ''; # camera model
  3163. $$self{CameraType} = ''; # Olympus camera type
  3164. if ($self->Options('HtmlDump')) {
  3165. require Image::ExifTool::HtmlDump;
  3166. $$self{HTML_DUMP} = new Image::ExifTool::HtmlDump;
  3167. }
  3168. # make sure our TextOut is a file reference
  3169. $$self{OPTIONS}{TextOut} = \*STDOUT unless ref $$self{OPTIONS}{TextOut};
  3170. }
  3171. #------------------------------------------------------------------------------
  3172. # Get tag key for next existing tag
  3173. # Inputs: 0) ExifTool ref, 1) tag key or case-sensitive tag name
  3174. # Returns: Key of next existing tag, or undef if no more
  3175. # Notes: This routine is provided for iterating through duplicate tags in the
  3176. # ValueConv of Composite tags.
  3177. sub NextTagKey($$)
  3178. {
  3179. my ($self, $tag) = @_;
  3180. my $i = ($tag =~ s/ \((\d+)\)$//) ? $1 + 1 : 1;
  3181. $tag = "$tag ($i)";
  3182. return $tag if defined $$self{VALUE}{$tag};
  3183. return undef;
  3184. }
  3185. #------------------------------------------------------------------------------
  3186. # Encode file name for calls to system i/o routines
  3187. # Inputs: 0) ExifTool ref, 1) file name, 2) flag to force conversion
  3188. # Returns: true if Windows Unicode routines should be used (in which case
  3189. # the file name will be encoded as a null-terminated UTF-16LE string)
  3190. sub EncodeFileName($$;$)
  3191. {
  3192. my ($self, $file, $force) = @_;
  3193. my $enc = $$self{OPTIONS}{CharsetFileName};
  3194. if ($enc and ($file =~ /[\x80-\xff]/ or $force)) {
  3195. # encode for use in Windows Unicode functions if necessary
  3196. if ($^O eq 'MSWin32') {
  3197. local $SIG{'__WARN__'} = \&SetWarning;
  3198. if (eval { require Win32API::File }) {
  3199. # recode as UTF-16LE and add null terminator
  3200. $_[1] = $self->Decode($file, $enc, undef, 'UTF16', 'II') . "\0\0";
  3201. return 1;
  3202. }
  3203. $self->WarnOnce('Install Win32API::File for Windows Unicode file support');
  3204. } else {
  3205. # recode as UTF-8 for other platforms if necessary
  3206. $_[1] = $self->Decode($file, $enc, undef, 'UTF8') unless $enc eq 'UTF8';
  3207. }
  3208. } elsif ($^O eq 'MSWin32' and $file =~ /[\x80-\xff]/) {
  3209. require Image::ExifTool::XMP;
  3210. if (Image::ExifTool::XMP::IsUTF8(\$file) < 0) {
  3211. $self->WarnOnce('FileName encoding not specified');
  3212. }
  3213. }
  3214. return 0;
  3215. }
  3216. #------------------------------------------------------------------------------
  3217. # Modified perl open() routine to properly handle special characters in file names
  3218. # Inputs: 0) ExifTool ref, 1) filehandle, 2) filename,
  3219. # 3) mode: '<' or undef = read, '>' = write, '+<' = update
  3220. # Returns: true on success
  3221. # Note: Must call like "$et->Open(\*FH,$file)", not "$et->Open(FH,$file)" to avoid
  3222. # "unopened filehandle" errors due to a change in scope of the filehandle
  3223. sub Open($*$;$)
  3224. {
  3225. my ($self, $fh, $file, $mode) = @_;
  3226. $file =~ s/^([\s&])/.\/$1/; # protect leading whitespace or ampersand
  3227. # default to read mode ('<') unless input is a pipe
  3228. $mode = ($file =~ /\|$/ ? '' : '<') unless $mode;
  3229. if ($mode) {
  3230. if ($self->EncodeFileName($file)) {
  3231. # handle Windows Unicode file name
  3232. local $SIG{'__WARN__'} = \&SetWarning;
  3233. my ($access, $create);
  3234. if ($mode eq '>') {
  3235. eval {
  3236. $access = Win32API::File::GENERIC_WRITE();
  3237. $create = Win32API::File::CREATE_ALWAYS();
  3238. }
  3239. } else {
  3240. eval {
  3241. $access = Win32API::File::GENERIC_READ();
  3242. $access |= Win32API::File::GENERIC_WRITE() if $mode eq '+<'; # update
  3243. $create = Win32API::File::OPEN_EXISTING();
  3244. }
  3245. }
  3246. my $wh = eval { Win32API::File::CreateFileW($file, $access, 0, [], $create, 0, []) };
  3247. return undef unless $wh;
  3248. my $fd = eval { Win32API::File::OsFHandleOpenFd($wh, 0) };
  3249. if (not defined $fd or $fd < 0) {
  3250. eval { Win32API::File::CloseHandle($wh) };
  3251. return undef;
  3252. }
  3253. $file = "&=$fd"; # specify file by descriptor
  3254. } else {
  3255. # add leading space to protect against leading characters like '>'
  3256. # in file name, and trailing "\0" to protect trailing spaces
  3257. $file = " $file\0";
  3258. }
  3259. }
  3260. return open $fh, "$mode$file";
  3261. }
  3262. #------------------------------------------------------------------------------
  3263. # Check to see if a file exists (with Windows Unicode support)
  3264. # Inputs: 0) ExifTool ref, 1) file name
  3265. # Returns: true if file exists
  3266. sub Exists($$)
  3267. {
  3268. my ($self, $file) = @_;
  3269. if ($self->EncodeFileName($file)) {
  3270. local $SIG{'__WARN__'} = \&SetWarning;
  3271. my $wh = eval { Win32API::File::CreateFileW($file,
  3272. Win32API::File::GENERIC_READ(), 0, [],
  3273. Win32API::File::OPEN_EXISTING(), 0, []) };
  3274. return 0 unless $wh;
  3275. eval { Win32API::File::CloseHandle($wh) };
  3276. } else {
  3277. return -e $file;
  3278. }
  3279. return 1;
  3280. }
  3281. #------------------------------------------------------------------------------
  3282. # Return true if file is a directory (with Windows Unicode support)
  3283. # Inputs: 0) ExifTool ref, 1) file name
  3284. # Returns: true if file is a directory (false if file isn't, or doesn't exist)
  3285. sub IsDirectory($$)
  3286. {
  3287. my ($et, $file) = @_;
  3288. if ($et->EncodeFileName($file)) {
  3289. local $SIG{'__WARN__'} = \&SetWarning;
  3290. my $attrs = eval { Win32API::File::GetFileAttributesW($file) };
  3291. my $dirBit = eval { Win32API::File::FILE_ATTRIBUTE_DIRECTORY() } || 0;
  3292. return 1 if $attrs and $attrs != 0xffffffff and $attrs & $dirBit;
  3293. } else {
  3294. return -d $file;
  3295. }
  3296. return 0;
  3297. }
  3298. #------------------------------------------------------------------------------
  3299. # Get file times (Unix seconds since the epoch)
  3300. # Inputs: 0) ExifTool ref, 1) file name or ref
  3301. # Returns: 0) access time, 1) modification time, 2) creation time (or undefs on error)
  3302. my $k32GetFileTime;
  3303. sub GetFileTime($$)
  3304. {
  3305. my ($self, $file) = @_;
  3306. # open file by name if necessary
  3307. unless (ref $file) {
  3308. local *FH;
  3309. $self->Open(\*FH, $file) or $self->Warn("GetFileTime error for '$file'"), return ();
  3310. $file = *FH; # (not \*FH, so *FH will be kept open until $file goes out of scope)
  3311. }
  3312. # on Windows, try to work around incorrect file times when daylight saving time is in effect
  3313. if ($^O eq 'MSWin32') {
  3314. if (not eval { require Win32::API }) {
  3315. $self->WarnOnce('Install Win32::API for proper handling of Windows file times');
  3316. } elsif (not eval { require Win32API::File }) {
  3317. $self->WarnOnce('Install Win32API::File for proper handling of Windows file times');
  3318. } else {
  3319. # get Win32 handle, needed for GetFileTime
  3320. my $win32Handle = eval { Win32API::File::GetOsFHandle($file) };
  3321. unless ($win32Handle) {
  3322. $self->Warn("Win32API::File::GetOsFHandle returned invalid handle");
  3323. return ();
  3324. }
  3325. # get FILETIME structs
  3326. my ($atime, $mtime, $ctime, $time);
  3327. $atime = $mtime = $ctime = pack 'LL', 0, 0;
  3328. unless ($k32GetFileTime) {
  3329. return () if defined $k32GetFileTime;
  3330. $k32GetFileTime = new Win32::API('KERNEL32', 'GetFileTime', 'NPPP', 'I');
  3331. unless ($k32GetFileTime) {
  3332. $self->Warn('Error calling Win32::API::GetFileTime');
  3333. $k32GetFileTime = 0;
  3334. return ();
  3335. }
  3336. }
  3337. unless ($k32GetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) {
  3338. $self->Warn("Win32::API::GetFileTime returned " . Win32::GetLastError());
  3339. return ();
  3340. }
  3341. # convert FILETIME structs to Unix seconds
  3342. foreach $time ($atime, $mtime, $ctime) {
  3343. my ($lo, $hi) = unpack 'LL', $time; # unpack FILETIME struct
  3344. # FILETIME is in 100 ns intervals since 0:00 UTC Jan 1, 1601
  3345. # (89 leap years between 1601 and 1970)
  3346. $time = ($hi * 4294967296 + $lo) * 1e-7 - (((1970-1601)*365+89)*24*3600);
  3347. }
  3348. return ($atime, $mtime, $ctime);
  3349. }
  3350. }
  3351. # other os (or Windows fallback)
  3352. return (stat $file)[8, 9, 10];
  3353. }
  3354. #------------------------------------------------------------------------------
  3355. # Parse function arguments and set member variables accordingly
  3356. # Inputs: Same as ImageInfo()
  3357. # - sets REQUESTED_TAGS, REQ_TAG_LOOKUP, IO_TAG_LIST, FILENAME, RAF, OPTIONS
  3358. sub ParseArguments($;@)
  3359. {
  3360. my $self = shift;
  3361. my $options = $$self{OPTIONS};
  3362. my @oldGroupOpts = grep /^Group/, keys %{$$self{OPTIONS}};
  3363. my (@exclude, $wasExcludeOpt);
  3364. $$self{REQUESTED_TAGS} = [ ];
  3365. $$self{REQ_TAG_LOOKUP} = { };
  3366. $$self{EXCL_TAG_LOOKUP} = { };
  3367. $$self{IO_TAG_LIST} = undef;
  3368. # handle our input arguments
  3369. while (@_) {
  3370. my $arg = shift;
  3371. if (ref $arg) {
  3372. if (ref $arg eq 'ARRAY') {
  3373. $$self{IO_TAG_LIST} = $arg;
  3374. foreach (@$arg) {
  3375. if (/^-(.*)/) {
  3376. push @exclude, $1;
  3377. } else {
  3378. push @{$$self{REQUESTED_TAGS}}, $_;
  3379. }
  3380. }
  3381. } elsif (ref $arg eq 'HASH') {
  3382. my $opt;
  3383. foreach $opt (keys %$arg) {
  3384. # a single new group option overrides all old group options
  3385. if (@oldGroupOpts and $opt =~ /^Group/) {
  3386. foreach (@oldGroupOpts) {
  3387. delete $$options{$_};
  3388. }
  3389. undef @oldGroupOpts;
  3390. }
  3391. $self->Options($opt, $$arg{$opt});
  3392. $opt eq 'Exclude' and $wasExcludeOpt = 1;
  3393. }
  3394. } elsif (ref $arg eq 'SCALAR' or UNIVERSAL::isa($arg,'GLOB')) {
  3395. next if defined $$self{RAF};
  3396. # convert image data from UTF-8 to character stream if necessary
  3397. # (patches RHEL 3 UTF8 LANG problem)
  3398. if (ref $arg eq 'SCALAR' and $] >= 5.006 and
  3399. (eval { require Encode; Encode::is_utf8($$arg) } or $@))
  3400. {
  3401. # repack by hand if Encode isn't available
  3402. my $buff = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$arg)) : Encode::encode('utf8',$$arg);
  3403. $arg = \$buff;
  3404. }
  3405. $$self{RAF} = new File::RandomAccess($arg);
  3406. # set filename to empty string to indicate that
  3407. # we have a file but we didn't open it
  3408. $$self{FILENAME} = '';
  3409. } elsif (UNIVERSAL::isa($arg, 'File::RandomAccess')) {
  3410. $$self{RAF} = $arg;
  3411. $$self{FILENAME} = '';
  3412. } else {
  3413. warn "Don't understand ImageInfo argument $arg\n";
  3414. }
  3415. } elsif (defined $$self{FILENAME}) {
  3416. if ($arg =~ /^-(.*)/) {
  3417. push @exclude, $1;
  3418. } else {
  3419. push @{$$self{REQUESTED_TAGS}}, $arg;
  3420. }
  3421. } else {
  3422. $$self{FILENAME} = $arg;
  3423. }
  3424. }
  3425. # add additional requested tags to lookup
  3426. if ($$options{RequestTags}) {
  3427. $$self{REQ_TAG_LOOKUP}{$_} = 1 foreach @{$$options{RequestTags}};
  3428. }
  3429. # expand shortcuts in tag arguments if provided
  3430. if (@{$$self{REQUESTED_TAGS}}) {
  3431. ExpandShortcuts($$self{REQUESTED_TAGS});
  3432. # initialize lookup for requested tags
  3433. foreach (@{$$self{REQUESTED_TAGS}}) {
  3434. /(^|:)([-\w?*]+)#?$/ and $$self{REQ_TAG_LOOKUP}{lc($2)} = 1;
  3435. }
  3436. }
  3437. if (@exclude or $wasExcludeOpt) {
  3438. # must add existing excluded tags
  3439. push @exclude, @{$$options{Exclude}} if $$options{Exclude};
  3440. $$options{Exclude} = \@exclude;
  3441. # expand shortcuts in new exclude list
  3442. ExpandShortcuts($$options{Exclude}, 1); # (also remove '#' suffix)
  3443. }
  3444. # generate lookup for excluded tags
  3445. if ($$options{Exclude}) {
  3446. foreach (@{$$options{Exclude}}) {
  3447. /([-\w]+)#?$/ and $$self{EXCL_TAG_LOOKUP}{lc($1)} = 1;
  3448. }
  3449. # exclude list is used only for EXCL_TAG_LOOKUP when TAGS_FROM_FILE is set
  3450. undef $$options{Exclude} if $$self{TAGS_FROM_FILE};
  3451. }
  3452. }
  3453. #------------------------------------------------------------------------------
  3454. # Get list of tags in specified group
  3455. # Inputs: 0) ExifTool ref, 1) group spec, 2) tag key or reference to list of tag keys
  3456. # Returns: list of matching tags in list context, or first match in scalar context
  3457. # Notes: Group spec may contain multiple groups separated by colons, each
  3458. # possibly with a leading family number
  3459. sub GroupMatches($$$)
  3460. {
  3461. my ($self, $group, $tagList) = @_;
  3462. $tagList = [ $tagList ] unless ref $tagList;
  3463. my ($tag, @matches);
  3464. if ($group =~ /:/) {
  3465. # check each group name individually (eg. "Author:1IPTC")
  3466. my @grps = split ':', lc $group;
  3467. my (@fmys, $g);
  3468. for ($g=0; $g<@grps; ++$g) {
  3469. $fmys[$g] = $1 if $grps[$g] =~ s/^(\d+)//;
  3470. }
  3471. foreach $tag (@$tagList) {
  3472. my @groups = $self->GetGroup($tag, -1);
  3473. for ($g=0; $g<@grps; ++$g) {
  3474. my $grp = $grps[$g];
  3475. next if $grp eq '*' or $grp eq 'all';
  3476. if (defined $fmys[$g]) {
  3477. my $f = $fmys[$g];
  3478. last unless $groups[$f] and $grps[$g] eq lc $groups[$f];
  3479. } else {
  3480. last unless grep /^$grps[$g]$/i, @groups;
  3481. }
  3482. }
  3483. if ($g == @grps) {
  3484. return $tag unless wantarray;
  3485. push @matches, $tag;
  3486. }
  3487. }
  3488. } else {
  3489. my $family = ($group =~ s/^(\d+)//) ? $1 : -1;
  3490. foreach $tag (@$tagList) {
  3491. my @groups = $self->GetGroup($tag, $family);
  3492. if (grep(/^$group$/i, @groups)) {
  3493. return $tag unless wantarray;
  3494. push @matches, $tag;
  3495. }
  3496. }
  3497. }
  3498. return wantarray ? @matches : $matches[0];
  3499. }
  3500. #------------------------------------------------------------------------------
  3501. # Set list of found tags from previously requested tags
  3502. # Inputs: 0) ExifTool object reference
  3503. # Returns: 0) Reference to list of found tag keys (in order of requested tags)
  3504. # 1) Reference to list of indices for tags requested by value
  3505. # 2) Reference to list of indices for tags specified by wildcard or "all"
  3506. # Notes: index lists are returned in increasing order
  3507. sub SetFoundTags($)
  3508. {
  3509. my $self = shift;
  3510. my $options = $$self{OPTIONS};
  3511. my $reqTags = $$self{REQUESTED_TAGS} || [ ];
  3512. my $duplicates = $$options{Duplicates};
  3513. my $exclude = $$options{Exclude};
  3514. my $fileOrder = $$self{FILE_ORDER};
  3515. my @groupOptions = sort grep /^Group/, keys %$options;
  3516. my $doDups = $duplicates || $exclude || @groupOptions;
  3517. my ($tag, $rtnTags, @byValue, @wildTags);
  3518. # only return requested tags if specified
  3519. if (@$reqTags) {
  3520. $rtnTags or $rtnTags = [ ];
  3521. # scan through the requested tags and generate a list of tags we found
  3522. my $tagHash = $$self{VALUE};
  3523. my $reqTag;
  3524. foreach $reqTag (@$reqTags) {
  3525. my (@matches, $group, $allGrp, $allTag, $byValue);
  3526. if ($reqTag =~ /^(.*):(.+)/) {
  3527. ($group, $tag) = ($1, $2);
  3528. if ($group =~ /^(\*|all)$/i) {
  3529. $allGrp = 1;
  3530. } elsif ($group !~ /^[-\w:]*$/) {
  3531. $self->Warn("Invalid group name '$group'");
  3532. $group = 'invalid';
  3533. }
  3534. } else {
  3535. $tag = $reqTag;
  3536. }
  3537. $byValue = 1 if $tag =~ s/#$// and $$options{PrintConv};
  3538. if (defined $$tagHash{$reqTag} and not $doDups) {
  3539. $matches[0] = $tag;
  3540. } elsif ($tag =~ /^(\*|all)$/i) {
  3541. # tag name of '*' or 'all' matches all tags
  3542. if ($doDups or $allGrp) {
  3543. @matches = grep(!/#/, keys %$tagHash);
  3544. } else {
  3545. @matches = grep(!/ /, keys %$tagHash);
  3546. }
  3547. next unless @matches; # don't want entry in list for '*' tag
  3548. $allTag = 1;
  3549. } elsif ($tag =~ /[*?]/) {
  3550. # allow wildcards in tag names
  3551. $tag =~ s/\*/[-\\w]*/g;
  3552. $tag =~ s/\?/[-\\w]/g;
  3553. $tag .= '( \\(.*)?' if $doDups or $allGrp;
  3554. @matches = grep(/^$tag$/i, keys %$tagHash);
  3555. next unless @matches; # don't want entry in list for wildcard tags
  3556. $allTag = 1;
  3557. } elsif ($doDups or defined $group) {
  3558. # must also look for tags like "Tag (1)"
  3559. # (but be sure not to match temporary ValueConv entries like "Tag #")
  3560. @matches = grep(/^$tag( \(|$)/i, keys %$tagHash);
  3561. } elsif ($tag =~ /^[-\w]+$/) {
  3562. # find first matching value
  3563. # (use in list context to return value instead of count)
  3564. ($matches[0]) = grep /^$tag$/i, keys %$tagHash;
  3565. defined $matches[0] or undef @matches;
  3566. } else {
  3567. $self->Warn("Invalid tag name '$tag'");
  3568. }
  3569. if (defined $group and not $allGrp) {
  3570. # keep only specified group
  3571. @matches = $self->GroupMatches($group, \@matches);
  3572. next unless @matches or not $allTag;
  3573. }
  3574. if (@matches > 1) {
  3575. # maintain original file order for multiple tags
  3576. @matches = sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @matches;
  3577. # return only the highest priority tag unless duplicates wanted
  3578. unless ($doDups or $allTag or $allGrp) {
  3579. $tag = shift @matches;
  3580. my $oldPriority = $$self{PRIORITY}{$tag} || 1;
  3581. foreach (@matches) {
  3582. my $priority = $$self{PRIORITY}{$_};
  3583. $priority = 1 unless defined $priority;
  3584. next unless $priority >= $oldPriority;
  3585. $tag = $_;
  3586. $oldPriority = $priority || 1;
  3587. }
  3588. @matches = ( $tag );
  3589. }
  3590. } elsif (not @matches) {
  3591. # put entry in return list even without value (value is undef)
  3592. $matches[0] = $byValue ? "$tag #(0)" : "$tag (0)";
  3593. # bogus file order entry to avoid warning if sorting in file order
  3594. $$self{FILE_ORDER}{$matches[0]} = 9999;
  3595. }
  3596. # save indices of tags extracted by value
  3597. push @byValue, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $byValue;
  3598. # save indices of wildcard tags
  3599. push @wildTags, scalar(@$rtnTags) .. (scalar(@$rtnTags)+scalar(@matches)-1) if $allTag;
  3600. push @$rtnTags, @matches;
  3601. }
  3602. } else {
  3603. # no requested tags, so we want all tags
  3604. my @allTags;
  3605. if ($doDups) {
  3606. @allTags = keys %{$$self{VALUE}};
  3607. } else {
  3608. # only include tag if it doesn't end in a copy number
  3609. @allTags = grep(!/ /, keys %{$$self{VALUE}});
  3610. }
  3611. $rtnTags = \@allTags;
  3612. }
  3613. # filter excluded tags and group options
  3614. while (($exclude or @groupOptions) and @$rtnTags) {
  3615. if ($exclude) {
  3616. my ($pat, %exclude);
  3617. foreach $pat (@$exclude) {
  3618. my $group;
  3619. if ($pat =~ /^(.*):(.+)/) {
  3620. ($group, $tag) = ($1, $2);
  3621. if ($group =~ /^(\*|all)$/i) {
  3622. undef $group;
  3623. } elsif ($group !~ /^[-\w:]*$/) {
  3624. $self->Warn("Invalid group name '$group'");
  3625. $group = 'invalid';
  3626. }
  3627. } else {
  3628. $tag = $pat;
  3629. }
  3630. my @matches;
  3631. if ($tag =~ /^(\*|all)$/i) {
  3632. @matches = @$rtnTags;
  3633. } else {
  3634. # allow wildcards in tag names
  3635. $tag =~ s/\*/[-\\w]*/g;
  3636. $tag =~ s/\?/[-\\w]/g;
  3637. @matches = grep(/^$tag( |$)/i, @$rtnTags);
  3638. }
  3639. @matches = $self->GroupMatches($group, \@matches) if $group and @matches;
  3640. $exclude{$_} = 1 foreach @matches;
  3641. }
  3642. if (%exclude) {
  3643. my @filteredTags;
  3644. $exclude{$_} or push @filteredTags, $_ foreach @$rtnTags;
  3645. $rtnTags = \@filteredTags; # use new filtered tag list
  3646. last unless @filteredTags; # all done if nothing left
  3647. }
  3648. last if $duplicates and not @groupOptions;
  3649. }
  3650. # filter groups if requested, or to remove duplicates
  3651. my (%keepTags, %wantGroup, $family, $groupOpt);
  3652. my $allGroups = 1;
  3653. # build hash of requested/excluded group names for each group family
  3654. my $wantOrder = 0;
  3655. foreach $groupOpt (@groupOptions) {
  3656. $groupOpt =~ /^Group(\d*(:\d+)*)/ or next;
  3657. $family = $1 || 0;
  3658. $wantGroup{$family} or $wantGroup{$family} = { };
  3659. my $groupList;
  3660. if (ref $$options{$groupOpt} eq 'ARRAY') {
  3661. $groupList = $$options{$groupOpt};
  3662. } else {
  3663. $groupList = [ $$options{$groupOpt} ];
  3664. }
  3665. foreach (@$groupList) {
  3666. # groups have priority in order they were specified
  3667. ++$wantOrder;
  3668. my ($groupName, $want);
  3669. if (/^-(.*)/) {
  3670. # excluded group begins with '-'
  3671. $groupName = $1;
  3672. $want = 0; # we don't want tags in this group
  3673. } else {
  3674. $groupName = $_;
  3675. $want = $wantOrder; # we want tags in this group
  3676. $allGroups = 0; # don't want all groups if we requested one
  3677. }
  3678. $wantGroup{$family}{$groupName} = $want;
  3679. }
  3680. }
  3681. # loop through all tags and decide which ones we want
  3682. my (@tags, %bestTag);
  3683. GR_TAG: foreach $tag (@$rtnTags) {
  3684. my $wantTag = $allGroups; # want tag by default if want all groups
  3685. foreach $family (keys %wantGroup) {
  3686. my $group = $self->GetGroup($tag, $family);
  3687. my $wanted = $wantGroup{$family}{$group};
  3688. next unless defined $wanted;
  3689. next GR_TAG unless $wanted; # skip tag if group excluded
  3690. # take lowest non-zero want flag
  3691. next if $wantTag and $wantTag < $wanted;
  3692. $wantTag = $wanted;
  3693. }
  3694. next unless $wantTag;
  3695. if ($duplicates) {
  3696. push @tags, $tag;
  3697. } else {
  3698. my $tagName = GetTagName($tag);
  3699. my $bestTag = $bestTag{$tagName};
  3700. if (defined $bestTag) {
  3701. next if $wantTag > $keepTags{$bestTag};
  3702. if ($wantTag == $keepTags{$bestTag}) {
  3703. # want two tags with the same name -- keep the latest one
  3704. if ($tag =~ / \((\d+)\)$/) {
  3705. my $tagNum = $1;
  3706. next if $bestTag !~ / \((\d+)\)$/ or $1 > $tagNum;
  3707. }
  3708. }
  3709. # this tag is better, so delete old best tag
  3710. delete $keepTags{$bestTag};
  3711. }
  3712. $keepTags{$tag} = $wantTag; # keep this tag (for now...)
  3713. $bestTag{$tagName} = $tag; # this is our current best tag
  3714. }
  3715. }
  3716. unless ($duplicates) {
  3717. # construct new tag list with no duplicates, preserving order
  3718. foreach $tag (@$rtnTags) {
  3719. push @tags, $tag if $keepTags{$tag};
  3720. }
  3721. }
  3722. $rtnTags = \@tags;
  3723. last;
  3724. }
  3725. $$self{FOUND_TAGS} = $rtnTags; # save found tags
  3726. # return reference to found tag keys (and list of indices of tags to extract by value)
  3727. return wantarray ? ($rtnTags, \@byValue, \@wildTags) : $rtnTags;
  3728. }
  3729. #------------------------------------------------------------------------------
  3730. # Utility to load our write routines if required (called via AUTOLOAD)
  3731. # Inputs: 0) autoload function, 1-N) function arguments
  3732. # Returns: result of function or dies if function not available
  3733. sub DoAutoLoad(@)
  3734. {
  3735. my $autoload = shift;
  3736. my @callInfo = split(/::/, $autoload);
  3737. my $file = 'Image/ExifTool/Write';
  3738. return if $callInfo[$#callInfo] eq 'DESTROY';
  3739. if (@callInfo == 4) {
  3740. # load Image/ExifTool/WriteMODULE.pl
  3741. $file .= "$callInfo[2].pl";
  3742. } elsif ($callInfo[-1] eq 'ShiftTime') {
  3743. $file = 'Image/ExifTool/Shift.pl'; # load Shift.pl
  3744. } else {
  3745. # load Image/ExifTool/Writer.pl
  3746. $file .= 'r.pl';
  3747. }
  3748. # attempt to load the package
  3749. eval { require $file } or die "Error while attempting to call $autoload\n$@\n";
  3750. unless (defined &$autoload) {
  3751. my @caller = caller(0);
  3752. # reproduce Perl's standard 'undefined subroutine' message:
  3753. die "Undefined subroutine $autoload called at $caller[1] line $caller[2]\n";
  3754. }
  3755. no strict 'refs';
  3756. return &$autoload(@_); # call the function
  3757. }
  3758. #------------------------------------------------------------------------------
  3759. # AutoLoad our writer routines when necessary
  3760. #
  3761. sub AUTOLOAD
  3762. {
  3763. return DoAutoLoad($AUTOLOAD, @_);
  3764. }
  3765. #------------------------------------------------------------------------------
  3766. # Add warning tag
  3767. # Inputs: 0) ExifTool object reference, 1) warning message
  3768. # 2) true if minor (2 if behaviour changes when warning is ignored)
  3769. # Returns: true if warning tag was added
  3770. sub Warn($$;$)
  3771. {
  3772. my ($self, $str, $ignorable) = @_;
  3773. if ($ignorable) {
  3774. return 0 if $$self{OPTIONS}{IgnoreMinorErrors};
  3775. $str = $ignorable eq '2' ? "[Minor] $str" : "[minor] $str";
  3776. }
  3777. $self->FoundTag('Warning', $str);
  3778. return 1;
  3779. }
  3780. #------------------------------------------------------------------------------
  3781. # Add warning tag only once per processed file
  3782. # Inputs: 0) ExifTool object reference, 1) warning message, 2) true if minor
  3783. # Returns: true if warning tag was added
  3784. sub WarnOnce($$;$)
  3785. {
  3786. my ($self, $str, $ignorable) = @_;
  3787. return 0 if $ignorable and $$self{OPTIONS}{IgnoreMinorErrors};
  3788. unless ($$self{WARNED_ONCE}{$str}) {
  3789. $self->Warn($str, $ignorable);
  3790. $$self{WARNED_ONCE}{$str} = 1;
  3791. }
  3792. return 1;
  3793. }
  3794. #------------------------------------------------------------------------------
  3795. # Add error tag
  3796. # Inputs: 0) ExifTool object reference, 1) error message, 2) true if minor
  3797. # Returns: true if error tag was added, otherwise warning was added
  3798. sub Error($$;$)
  3799. {
  3800. my ($self, $str, $ignorable) = @_;
  3801. if ($$self{DemoteErrors}) {
  3802. $self->Warn($str) and ++$$self{DemoteErrors};
  3803. return 1;
  3804. } elsif ($ignorable) {
  3805. $$self{OPTIONS}{IgnoreMinorErrors} and $self->Warn($str), return 0;
  3806. $str = "[minor] $str";
  3807. }
  3808. $self->FoundTag('Error', $str);
  3809. return 1;
  3810. }
  3811. #------------------------------------------------------------------------------
  3812. # Expand shortcuts
  3813. # Inputs: 0) reference to list of tags, 1) set to remove trailing '#'
  3814. # Notes: Handles leading '-' for excluded tags, trailing '#' for ValueConv,
  3815. # multiple group names, and redirected tags
  3816. sub ExpandShortcuts($;$)
  3817. {
  3818. my ($tagList, $removeSuffix) = @_;
  3819. return unless $tagList and @$tagList;
  3820. require Image::ExifTool::Shortcuts;
  3821. # expand shortcuts
  3822. my $suffix = $removeSuffix ? '' : '#';
  3823. my @expandedTags;
  3824. my ($entry, $tag, $excl);
  3825. foreach $entry (@$tagList) {
  3826. # skip things like options hash references in list
  3827. if (ref $entry) {
  3828. push @expandedTags, $entry;
  3829. next;
  3830. }
  3831. # remove leading '-'
  3832. ($excl, $tag) = $entry =~ /^(-?)(.*)/s;
  3833. my ($post, @post, $pre, $v);
  3834. # handle redirection
  3835. if (not $excl and $tag =~ /(.+?)([-+]?[<>].+)/s) {
  3836. ($tag, $post) = ($1, $2);
  3837. if ($post =~ /^[-+]?>/ or $post !~ /\$/) {
  3838. # expand shortcuts in postfix (rhs of redirection)
  3839. my ($op, $p2, $t2) = ($post =~ /([-+]?[<>])(.+:)?(.+)/);
  3840. $p2 = '' unless defined $p2;
  3841. $v = ($t2 =~ s/#$//) ? $suffix : ''; # ValueConv suffix
  3842. my ($match) = grep /^\Q$t2\E$/i, keys %Image::ExifTool::Shortcuts::Main;
  3843. if ($match) {
  3844. foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  3845. /^-/ and next; # ignore excluded tags
  3846. if ($p2 and /(.+:)(.+)/) {
  3847. push @post, "$op$_$v";
  3848. } else {
  3849. push @post, "$op$p2$_$v";
  3850. }
  3851. }
  3852. next unless @post;
  3853. $post = shift @post;
  3854. }
  3855. }
  3856. } else {
  3857. $post = '';
  3858. }
  3859. # handle group names
  3860. if ($tag =~ /(.+:)(.+)/) {
  3861. ($pre, $tag) = ($1, $2);
  3862. } else {
  3863. $pre = '';
  3864. }
  3865. $v = ($tag =~ s/#$//) ? $suffix : ''; # ValueConv suffix
  3866. # loop over all postfixes
  3867. for (;;) {
  3868. # expand the tag name
  3869. my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
  3870. if ($match) {
  3871. if ($excl) {
  3872. # entry starts with '-', so exclude all tags in this shortcut
  3873. foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  3874. /^-/ and next; # ignore excluded exclude tags
  3875. # group of expanded tag takes precedence
  3876. if ($pre and /(.+:)(.+)/) {
  3877. push @expandedTags, "$excl$_";
  3878. } else {
  3879. push @expandedTags, "$excl$pre$_";
  3880. }
  3881. }
  3882. } elsif (length $pre or length $post or $v) {
  3883. foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
  3884. /(-?)(.+:)?(.+)/;
  3885. if ($2) {
  3886. # group from expanded tag takes precedence
  3887. push @expandedTags, "$_$v$post";
  3888. } else {
  3889. push @expandedTags, "$1$pre$3$v$post";
  3890. }
  3891. }
  3892. } else {
  3893. push @expandedTags, @{$Image::ExifTool::Shortcuts::Main{$match}};
  3894. }
  3895. } else {
  3896. push @expandedTags, "$excl$pre$tag$v$post";
  3897. }
  3898. last unless @post;
  3899. $post = shift @post;
  3900. }
  3901. }
  3902. @$tagList = @expandedTags;
  3903. }
  3904. #------------------------------------------------------------------------------
  3905. # Add hash of Composite tags to our composites
  3906. # Inputs: 0) hash reference to table of Composite tags to add or module name,
  3907. # 1) overwrite existing tag
  3908. sub AddCompositeTags($;$)
  3909. {
  3910. local $_;
  3911. my ($add, $overwrite) = @_;
  3912. my $module;
  3913. unless (ref $add) {
  3914. $module = $add;
  3915. $add .= '::Composite';
  3916. no strict 'refs';
  3917. $add = \%$add;
  3918. }
  3919. my $defaultGroups = $$add{GROUPS};
  3920. # MUST get the main Composite table so fetching it later doesn't override our TagID's
  3921. # (which may be different from the table keys if there were duplicates)
  3922. my $compTable = GetTagTable('Image::ExifTool::Composite');
  3923. # make sure default groups are defined in families 0 and 1
  3924. if ($defaultGroups) {
  3925. $$defaultGroups{0} or $$defaultGroups{0} = 'Composite';
  3926. $$defaultGroups{1} or $$defaultGroups{1} = 'Composite';
  3927. $$defaultGroups{2} or $$defaultGroups{2} = 'Other';
  3928. } else {
  3929. $defaultGroups = $$add{GROUPS} = { 0 => 'Composite', 1 => 'Composite', 2 => 'Other' };
  3930. }
  3931. SetupTagTable($add); # generate Name, TagID, etc
  3932. my $tagID;
  3933. foreach $tagID (sort keys %$add) {
  3934. next if $specialTags{$tagID}; # must skip special tags
  3935. my $tagInfo = $$add{$tagID};
  3936. # tagID's MUST be the exact tag name for logic in BuildCompositeTags()
  3937. my $tag = $$tagInfo{Name};
  3938. $$tagInfo{Module} = $module if $$tagInfo{Writable};
  3939. # allow Composite tags with the same name
  3940. my ($t, $n, $type);
  3941. while ($$compTable{$tag} and not $overwrite) {
  3942. $n ? $n += 1 : ($n = 2, $t = $tag);
  3943. $tag = "${t}-$n";
  3944. $$tagInfo{NewTagID} = $tag; # save new ID so we can use it in TagLookup
  3945. }
  3946. # convert scalar Require/Desire entries
  3947. foreach $type ('Require','Desire') {
  3948. my $req = $$tagInfo{$type} or next;
  3949. $$tagInfo{$type} = { 0 => $req } if ref($req) ne 'HASH';
  3950. }
  3951. # add this Composite tag to our main Composite table
  3952. $$tagInfo{Table} = $compTable;
  3953. # (use the original TagID, even if we changed it, so don't do this:)
  3954. # $$tagInfo{TagID} = $tag;
  3955. # save tag under NewTagID in Composite table
  3956. $$compTable{$tag} = $tagInfo;
  3957. # set all default groups in tag
  3958. my $groups = $$tagInfo{Groups};
  3959. $groups or $groups = $$tagInfo{Groups} = { };
  3960. # fill in default groups
  3961. foreach (keys %$defaultGroups) {
  3962. $$groups{$_} or $$groups{$_} = $$defaultGroups{$_};
  3963. }
  3964. # set flag indicating group list was built
  3965. $$tagInfo{GotGroups} = 1;
  3966. }
  3967. }
  3968. #------------------------------------------------------------------------------
  3969. # Add tags to TagLookup (used for writing)
  3970. # Inputs: 0) source hash of tag definitions, 1) name of destination tag table
  3971. sub AddTagsToLookup($$)
  3972. {
  3973. my ($tagHash, $table) = @_;
  3974. if (defined &Image::ExifTool::TagLookup::AddTags) {
  3975. Image::ExifTool::TagLookup::AddTags($tagHash, $table);
  3976. } elsif (not $Image::ExifTool::pluginTags{$tagHash}) {
  3977. # queue these tags until TagLookup is loaded
  3978. push @Image::ExifTool::pluginTags, [ $tagHash, $table ];
  3979. # set flag so we don't load same tags twice
  3980. $Image::ExifTool::pluginTags{$tagHash} = 1;
  3981. }
  3982. }
  3983. #------------------------------------------------------------------------------
  3984. # Expand tagInfo Flags
  3985. # Inputs: 0) tagInfo hash ref
  3986. # Notes: $$tagInfo{Flags} must be defined to call this routine
  3987. sub ExpandFlags($)
  3988. {
  3989. my $tagInfo = shift;
  3990. my $flags = $$tagInfo{Flags};
  3991. if (ref $flags eq 'ARRAY') {
  3992. foreach (@$flags) {
  3993. $$tagInfo{$_} = 1;
  3994. }
  3995. } elsif (ref $flags eq 'HASH') {
  3996. my $key;
  3997. foreach $key (keys %$flags) {
  3998. $$tagInfo{$key} = $$flags{$key};
  3999. }
  4000. } else {
  4001. $$tagInfo{$flags} = 1;
  4002. }
  4003. }
  4004. #------------------------------------------------------------------------------
  4005. # Set up tag table (must be done once for each tag table used)
  4006. # Inputs: 0) Reference to tag table
  4007. # Notes: - generates 'Name' field from key if it doesn't exist
  4008. # - stores 'Table' pointer and 'TagID' value
  4009. # - expands 'Flags' for quick lookup
  4010. sub SetupTagTable($)
  4011. {
  4012. my $tagTablePtr = shift;
  4013. my ($tagID, $tagInfo);
  4014. foreach $tagID (TagTableKeys($tagTablePtr)) {
  4015. my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
  4016. # process conditional tagInfo arrays
  4017. foreach $tagInfo (@infoArray) {
  4018. $$tagInfo{Table} = $tagTablePtr;
  4019. $$tagInfo{TagID} = $tagID;
  4020. $$tagInfo{Name} or $$tagInfo{Name} = MakeTagName($tagID);
  4021. $$tagInfo{Flags} and ExpandFlags($tagInfo);
  4022. }
  4023. next unless @infoArray > 1;
  4024. # add an "Index" member to each tagInfo in a list
  4025. my $index = 0;
  4026. foreach $tagInfo (@infoArray) {
  4027. $$tagInfo{Index} = $index++;
  4028. }
  4029. }
  4030. }
  4031. #------------------------------------------------------------------------------
  4032. # Utilities to check for numerical types
  4033. # Inputs: 0) value; Returns: true if value is a numerical type
  4034. # Notes: May change commas to decimals in floats for use in other locales
  4035. sub IsFloat($) {
  4036. return 1 if $_[0] =~ /^[+-]?(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
  4037. # allow comma separators (for other locales)
  4038. return 0 unless $_[0] =~ /^[+-]?(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/;
  4039. $_[0] =~ tr/,/./; # but translate ',' to '.'
  4040. return 1;
  4041. }
  4042. sub IsInt($) { return scalar($_[0] =~ /^[+-]?\d+$/); }
  4043. sub IsHex($) { return scalar($_[0] =~ /^(0x)?[0-9a-f]{1,8}$/i); }
  4044. sub IsRational($) { return scalar($_[0] =~ m{^[-+]?\d+/\d+$}); }
  4045. # round floating point value to specified number of significant digits
  4046. # Inputs: 0) value, 1) number of sig digits; Returns: rounded number
  4047. sub RoundFloat($$)
  4048. {
  4049. my ($val, $sig) = @_;
  4050. return sprintf("%.${sig}g", $val);
  4051. }
  4052. # Convert strings to floating point numbers (or undef)
  4053. # Inputs: 0-N) list of strings (may be undef)
  4054. # Returns: last value converted
  4055. sub ToFloat(@)
  4056. {
  4057. local $_;
  4058. foreach (@_) {
  4059. next unless defined $_;
  4060. # (add 0 to convert "0.0" to "0" for tests)
  4061. $_ = /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?)/ ? $1 + 0 : undef;
  4062. }
  4063. return $_[-1];
  4064. }
  4065. #------------------------------------------------------------------------------
  4066. # Utility routines to for reading binary data values from file
  4067. my %unpackMotorola = ( S => 'n', L => 'N', C => 'C', c => 'c' );
  4068. my %unpackIntel = ( S => 'v', L => 'V', C => 'C', c => 'c' );
  4069. my %unpackRev = ( N => 'V', V => 'N', C => 'C', n => 'v', v => 'n', c => 'c' );
  4070. # the following 4 variables are defined in 'use vars' instead of using 'my'
  4071. # because mod_perl 5.6.1 apparently has a problem with setting file-scope 'my'
  4072. # variables from within subroutines (ref communication with Pavel Merdin):
  4073. # $swapBytes - set if EXIF header is not native byte ordering
  4074. # $swapWords - swap 32-bit words in doubles (ARM quirk)
  4075. $currentByteOrder = 'MM'; # current byte ordering ('II' or 'MM')
  4076. %unpackStd = %unpackMotorola;
  4077. # Swap bytes in data if necessary
  4078. # Inputs: 0) data, 1) number of bytes
  4079. # Returns: swapped data
  4080. sub SwapBytes($$)
  4081. {
  4082. return $_[0] unless $swapBytes;
  4083. my ($val, $bytes) = @_;
  4084. my $newVal = '';
  4085. $newVal .= substr($val, $bytes, 1) while $bytes--;
  4086. return $newVal;
  4087. }
  4088. # Swap words. Inputs: 8 bytes of data, Returns: swapped data
  4089. sub SwapWords($)
  4090. {
  4091. return $_[0] unless $swapWords and length($_[0]) == 8;
  4092. return substr($_[0],4,4) . substr($_[0],0,4)
  4093. }
  4094. # Unpack value, letting unpack() handle byte swapping
  4095. # Inputs: 0) unpack template, 1) data reference, 2) offset
  4096. # Returns: unpacked number
  4097. # - uses value of %unpackStd to determine the unpack template
  4098. # - can only be called for 'S' or 'L' templates since these are the only
  4099. # templates for which you can specify the byte ordering.
  4100. sub DoUnpackStd(@)
  4101. {
  4102. $_[2] and return unpack("x$_[2] $unpackStd{$_[0]}", ${$_[1]});
  4103. return unpack($unpackStd{$_[0]}, ${$_[1]});
  4104. }
  4105. # same, but with reversed byte order
  4106. sub DoUnpackRev(@)
  4107. {
  4108. my $fmt = $unpackRev{$unpackStd{$_[0]}};
  4109. $_[2] and return unpack("x$_[2] $fmt", ${$_[1]});
  4110. return unpack($fmt, ${$_[1]});
  4111. }
  4112. # Pack value
  4113. # Inputs: 0) template, 1) value, 2) data ref (or undef), 3) offset (if data ref)
  4114. # Returns: packed value
  4115. sub DoPackStd(@)
  4116. {
  4117. my $val = pack($unpackStd{$_[0]}, $_[1]);
  4118. $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val;
  4119. return $val;
  4120. }
  4121. # same, but with reversed byte order
  4122. sub DoPackRev(@)
  4123. {
  4124. my $val = pack($unpackRev{$unpackStd{$_[0]}}, $_[1]);
  4125. $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val;
  4126. return $val;
  4127. }
  4128. # Unpack value, handling the byte swapping manually
  4129. # Inputs: 0) # bytes, 1) unpack template, 2) data reference, 3) offset
  4130. # Returns: unpacked number
  4131. # - uses value of $swapBytes to determine byte ordering
  4132. sub DoUnpack(@)
  4133. {
  4134. my ($bytes, $template, $dataPt, $pos) = @_;
  4135. my $val;
  4136. if ($swapBytes) {
  4137. $val = '';
  4138. $val .= substr($$dataPt,$pos+$bytes,1) while $bytes--;
  4139. } else {
  4140. $val = substr($$dataPt,$pos,$bytes);
  4141. }
  4142. defined($val) or return undef;
  4143. return unpack($template,$val);
  4144. }
  4145. # Unpack double value
  4146. # Inputs: 0) unpack template, 1) data reference, 2) offset
  4147. # Returns: unpacked number
  4148. sub DoUnpackDbl(@)
  4149. {
  4150. my ($template, $dataPt, $pos) = @_;
  4151. my $val = substr($$dataPt,$pos,8);
  4152. defined($val) or return undef;
  4153. # swap bytes and 32-bit words (ARM quirk) if necessary, then unpack value
  4154. return unpack($template, SwapWords(SwapBytes($val, 8)));
  4155. }
  4156. # Inputs: 0) data reference, 1) offset into data
  4157. sub Get8s($$) { return DoUnpackStd('c', @_); }
  4158. sub Get8u($$) { return DoUnpackStd('C', @_); }
  4159. sub Get16s($$) { return DoUnpack(2, 's', @_); }
  4160. sub Get16u($$) { return DoUnpackStd('S', @_); }
  4161. sub Get32s($$) { return DoUnpack(4, 'l', @_); }
  4162. sub Get32u($$) { return DoUnpackStd('L', @_); }
  4163. sub GetFloat($$) { return DoUnpack(4, 'f', @_); }
  4164. sub GetDouble($$) { return DoUnpackDbl('d', @_); }
  4165. sub Get16uRev($$) { return DoUnpackRev('S', @_); }
  4166. sub Get32uRev($$) { return DoUnpackRev('L', @_); }
  4167. # rationals may be a floating point number, 'inf' or 'undef'
  4168. my ($ratNumer, $ratDenom);
  4169. sub GetRational32s($$)
  4170. {
  4171. my ($dataPt, $pos) = @_;
  4172. $ratNumer = Get16s($dataPt,$pos);
  4173. $ratDenom = Get16s($dataPt, $pos + 2) or return $ratNumer ? 'inf' : 'undef';
  4174. # round off to a reasonable number of significant figures
  4175. return RoundFloat($ratNumer / $ratDenom, 7);
  4176. }
  4177. sub GetRational32u($$)
  4178. {
  4179. my ($dataPt, $pos) = @_;
  4180. $ratNumer = Get16u($dataPt,$pos);
  4181. $ratDenom = Get16u($dataPt, $pos + 2) or return $ratNumer ? 'inf' : 'undef';
  4182. return RoundFloat($ratNumer / $ratDenom, 7);
  4183. }
  4184. sub GetRational64s($$)
  4185. {
  4186. my ($dataPt, $pos) = @_;
  4187. $ratNumer = Get32s($dataPt,$pos);
  4188. $ratDenom = Get32s($dataPt, $pos + 4) or return $ratNumer ? 'inf' : 'undef';
  4189. return RoundFloat($ratNumer / $ratDenom, 10);
  4190. }
  4191. sub GetRational64u($$)
  4192. {
  4193. my ($dataPt, $pos) = @_;
  4194. $ratNumer = Get32u($dataPt,$pos);
  4195. $ratDenom = Get32u($dataPt, $pos + 4) or return $ratNumer ? 'inf' : 'undef';
  4196. return RoundFloat($ratNumer / $ratDenom, 10);
  4197. }
  4198. sub GetFixed16s($$)
  4199. {
  4200. my ($dataPt, $pos) = @_;
  4201. my $val = Get16s($dataPt, $pos) / 0x100;
  4202. return int($val * 1000 + ($val<0 ? -0.5 : 0.5)) / 1000;
  4203. }
  4204. sub GetFixed16u($$)
  4205. {
  4206. my ($dataPt, $pos) = @_;
  4207. return int((Get16u($dataPt, $pos) / 0x100) * 1000 + 0.5) / 1000;
  4208. }
  4209. sub GetFixed32s($$)
  4210. {
  4211. my ($dataPt, $pos) = @_;
  4212. my $val = Get32s($dataPt, $pos) / 0x10000;
  4213. # remove insignificant digits
  4214. return int($val * 1e5 + ($val>0 ? 0.5 : -0.5)) / 1e5;
  4215. }
  4216. sub GetFixed32u($$)
  4217. {
  4218. my ($dataPt, $pos) = @_;
  4219. # remove insignificant digits
  4220. return int((Get32u($dataPt, $pos) / 0x10000) * 1e5 + 0.5) / 1e5;
  4221. }
  4222. # Inputs: 0) value, 1) data ref, 2) offset
  4223. sub Set8s(@) { return DoPackStd('c', @_); }
  4224. sub Set8u(@) { return DoPackStd('C', @_); }
  4225. sub Set16u(@) { return DoPackStd('S', @_); }
  4226. sub Set32u(@) { return DoPackStd('L', @_); }
  4227. sub Set16uRev(@) { return DoPackRev('S', @_); }
  4228. #------------------------------------------------------------------------------
  4229. # Get current byte order ('II' or 'MM')
  4230. sub GetByteOrder() { return $currentByteOrder; }
  4231. #------------------------------------------------------------------------------
  4232. # Set byte ordering
  4233. # Inputs: 0) 'MM'=motorola, 'II'=intel (will translate 'BigEndian', 'LittleEndian')
  4234. # Returns: 1 on success
  4235. sub SetByteOrder($)
  4236. {
  4237. my $order = shift;
  4238. if ($order eq 'MM') { # big endian (Motorola)
  4239. %unpackStd = %unpackMotorola;
  4240. } elsif ($order eq 'II') { # little endian (Intel)
  4241. %unpackStd = %unpackIntel;
  4242. } elsif ($order =~ /^Big/i) {
  4243. $order = 'MM';
  4244. %unpackStd = %unpackMotorola;
  4245. } elsif ($order =~ /^Little/i) {
  4246. $order = 'II';
  4247. %unpackStd = %unpackIntel;
  4248. } else {
  4249. return 0;
  4250. }
  4251. my $val = unpack('S','A ');
  4252. my $nativeOrder;
  4253. if ($val == 0x4120) { # big endian
  4254. $nativeOrder = 'MM';
  4255. } elsif ($val == 0x2041) { # little endian
  4256. $nativeOrder = 'II';
  4257. } else {
  4258. warn sprintf("Unknown native byte order! (pattern %x)\n",$val);
  4259. return 0;
  4260. }
  4261. $currentByteOrder = $order; # save current byte order
  4262. # swap bytes if our native CPU byte ordering is not the same as the EXIF
  4263. $swapBytes = ($order ne $nativeOrder);
  4264. # little-endian ARM has big-endian words for doubles (thanks Riku Voipio)
  4265. # (Note: Riku's patch checked for '0ff3', but I think it should be 'f03f' since
  4266. # 1 is '000000000000f03f' on an x86 -- so check for both, but which is correct?)
  4267. my $pack1d = pack('d', 1);
  4268. $swapWords = ($pack1d eq "\0\0\x0f\xf3\0\0\0\0" or
  4269. $pack1d eq "\0\0\xf0\x3f\0\0\0\0");
  4270. return 1;
  4271. }
  4272. #------------------------------------------------------------------------------
  4273. # Change byte order
  4274. sub ToggleByteOrder()
  4275. {
  4276. SetByteOrder(GetByteOrder() eq 'II' ? 'MM' : 'II');
  4277. }
  4278. #------------------------------------------------------------------------------
  4279. # hash lookups for reading values from data
  4280. my %formatSize = (
  4281. int8s => 1,
  4282. int8u => 1,
  4283. int16s => 2,
  4284. int16u => 2,
  4285. int16uRev => 2,
  4286. int32s => 4,
  4287. int32u => 4,
  4288. int32uRev => 4,
  4289. int64s => 8,
  4290. int64u => 8,
  4291. rational32s => 4,
  4292. rational32u => 4,
  4293. rational64s => 8,
  4294. rational64u => 8,
  4295. fixed16s => 2,
  4296. fixed16u => 2,
  4297. fixed32s => 4,
  4298. fixed32u => 4,
  4299. float => 4,
  4300. double => 8,
  4301. extended => 10,
  4302. unicode => 2,
  4303. complex => 8,
  4304. string => 1,
  4305. binary => 1,
  4306. 'undef' => 1,
  4307. ifd => 4,
  4308. ifd64 => 8,
  4309. );
  4310. my %readValueProc = (
  4311. int8s => \&Get8s,
  4312. int8u => \&Get8u,
  4313. int16s => \&Get16s,
  4314. int16u => \&Get16u,
  4315. int16uRev => \&Get16uRev,
  4316. int32s => \&Get32s,
  4317. int32u => \&Get32u,
  4318. int32uRev => \&Get32uRev,
  4319. int64s => \&Get64s,
  4320. int64u => \&Get64u,
  4321. rational32s => \&GetRational32s,
  4322. rational32u => \&GetRational32u,
  4323. rational64s => \&GetRational64s,
  4324. rational64u => \&GetRational64u,
  4325. fixed16s => \&GetFixed16s,
  4326. fixed16u => \&GetFixed16u,
  4327. fixed32s => \&GetFixed32s,
  4328. fixed32u => \&GetFixed32u,
  4329. float => \&GetFloat,
  4330. double => \&GetDouble,
  4331. extended => \&GetExtended,
  4332. ifd => \&Get32u,
  4333. ifd64 => \&Get64u,
  4334. );
  4335. # lookup for all rational types
  4336. my %isRational = (
  4337. rational32u => 1,
  4338. rational32s => 1,
  4339. rational64u => 1,
  4340. rational64s => 1,
  4341. );
  4342. sub FormatSize($) { return $formatSize{$_[0]}; }
  4343. #------------------------------------------------------------------------------
  4344. # Read value from binary data (with current byte ordering)
  4345. # Inputs: 0) data reference, 1) value offset, 2) format string,
  4346. # 3) number of values (or undef to use all data)
  4347. # 4) valid data length relative to offset, 5) optional pointer to returned rational
  4348. # Returns: converted value, or undefined if data isn't there
  4349. # or list of values in list context
  4350. sub ReadValue($$$$$;$)
  4351. {
  4352. my ($dataPt, $offset, $format, $count, $size, $ratPt) = @_;
  4353. my $len = $formatSize{$format};
  4354. unless ($len) {
  4355. warn "Unknown format $format";
  4356. $len = 1;
  4357. }
  4358. unless ($count) {
  4359. return '' if defined $count or $size < $len;
  4360. $count = int($size / $len);
  4361. }
  4362. # make sure entry is inside data
  4363. if ($len * $count > $size) {
  4364. $count = int($size / $len); # shorten count if necessary
  4365. $count < 1 and return undef; # return undefined if no data
  4366. }
  4367. my @vals;
  4368. my $proc = $readValueProc{$format};
  4369. if (not $proc) {
  4370. # handle undef/binary/string (also unsupported unicode/complex)
  4371. $vals[0] = substr($$dataPt, $offset, $count * $len);
  4372. # truncate string at null terminator if necessary
  4373. $vals[0] =~ s/\0.*//s if $format eq 'string';
  4374. } elsif ($isRational{$format} and $ratPt) {
  4375. # store rationals separately as string fractions
  4376. my @rat;
  4377. for (;;) {
  4378. push @vals, &$proc($dataPt, $offset);
  4379. push @rat, "$ratNumer/$ratDenom";
  4380. last if --$count <= 0;
  4381. $offset += $len;
  4382. }
  4383. $$ratPt = join(' ',@rat);
  4384. } else {
  4385. for (;;) {
  4386. push @vals, &$proc($dataPt, $offset);
  4387. last if --$count <= 0;
  4388. $offset += $len;
  4389. }
  4390. }
  4391. return @vals if wantarray;
  4392. return join(' ', @vals) if @vals > 1;
  4393. return $vals[0];
  4394. }
  4395. #------------------------------------------------------------------------------
  4396. # Decode string with specified encoding
  4397. # Inputs: 0) ExifTool object ref, 1) string to decode
  4398. # 2) source character set name (undef for current Charset)
  4399. # 3) optional source byte order (2-byte and 4-byte fixed-width sets only)
  4400. # 4) optional destination character set (defaults to Charset setting)
  4401. # 5) optional destination byte order (2-byte and 4-byte fixed-width only)
  4402. # Returns: string in destination encoding
  4403. # Note: ExifTool ref may be undef if character both character sets are provided
  4404. # (but in this case no warnings will be issued)
  4405. sub Decode($$$;$$$)
  4406. {
  4407. my ($self, $val, $from, $fromOrder, $to, $toOrder) = @_;
  4408. $from or $from = $$self{OPTIONS}{Charset};
  4409. $to or $to = $$self{OPTIONS}{Charset};
  4410. if ($from ne $to and length $val) {
  4411. require Image::ExifTool::Charset;
  4412. my $cs1 = $Image::ExifTool::Charset::csType{$from};
  4413. my $cs2 = $Image::ExifTool::Charset::csType{$to};
  4414. if ($cs1 and $cs2 and not $cs2 & 0x002) {
  4415. # treat as straight ASCII if no character will need remapping
  4416. if (($cs1 | $cs2) & 0x680 or $val =~ /[\x80-\xff]/) {
  4417. my $uni = Image::ExifTool::Charset::Decompose($self, $val, $from, $fromOrder);
  4418. $val = Image::ExifTool::Charset::Recompose($self, $uni, $to, $toOrder);
  4419. }
  4420. } elsif ($self) {
  4421. my $set = $cs1 ? $to : $from;
  4422. unless ($$self{"DecodeWarn$set"}) {
  4423. $self->Warn("Unsupported character set ($set)");
  4424. $$self{"DecodeWarn$set"} = 1;
  4425. }
  4426. }
  4427. }
  4428. return $val;
  4429. }
  4430. #------------------------------------------------------------------------------
  4431. # Encode string with specified encoding
  4432. # Inputs: 0) ExifTool object ref, 1) string, 2) destination character set name,
  4433. # 3) optional destination byte order (2-byte and 4-byte fixed-width sets only)
  4434. # Returns: string in specified encoding
  4435. sub Encode($$$;$)
  4436. {
  4437. my ($self, $val, $to, $toOrder) = @_;
  4438. return $self->Decode($val, undef, undef, $to, $toOrder);
  4439. }
  4440. #------------------------------------------------------------------------------
  4441. # Decode bit mask
  4442. # Inputs: 0) value to decode, 1) Reference to hash for decoding (or undef)
  4443. # 2) optional bits per word (defaults to 32)
  4444. sub DecodeBits($$;$)
  4445. {
  4446. my ($vals, $lookup, $bits) = @_;
  4447. $bits or $bits = 32;
  4448. my ($val, $i, @bitList);
  4449. my $num = 0;
  4450. foreach $val (split ' ', $vals) {
  4451. for ($i=0; $i<$bits; ++$i) {
  4452. next unless $val & (1 << $i);
  4453. my $n = $i + $num;
  4454. if (not $lookup) {
  4455. push @bitList, $n;
  4456. } elsif ($$lookup{$n}) {
  4457. push @bitList, $$lookup{$n};
  4458. } else {
  4459. push @bitList, "[$n]";
  4460. }
  4461. }
  4462. $num += $bits;
  4463. }
  4464. return '(none)' unless @bitList;
  4465. return join($lookup ? ', ' : ',', @bitList);
  4466. }
  4467. #------------------------------------------------------------------------------
  4468. # Validate an extracted image and repair if necessary
  4469. # Inputs: 0) ExifTool object reference, 1) image reference, 2) tag name or key
  4470. # Returns: image reference or undef if it wasn't valid
  4471. # Note: should be called from RawConv, not ValueConv
  4472. sub ValidateImage($$$)
  4473. {
  4474. my ($self, $imagePt, $tag) = @_;
  4475. return undef if $$imagePt eq 'none';
  4476. unless ($$imagePt =~ /^(Binary data|\xff\xd8\xff)/ or
  4477. # the first byte of the preview of some Minolta cameras is wrong,
  4478. # so check for this and set it back to 0xff if necessary
  4479. $$imagePt =~ s/^.(\xd8\xff\xdb)/\xff$1/s or
  4480. $self->Options('IgnoreMinorErrors'))
  4481. {
  4482. # issue warning only if the tag was specifically requested
  4483. if ($$self{REQ_TAG_LOOKUP}{lc GetTagName($tag)}) {
  4484. $self->Warn("$tag is not a valid JPEG image",1);
  4485. return undef;
  4486. }
  4487. }
  4488. return $imagePt;
  4489. }
  4490. #------------------------------------------------------------------------------
  4491. # Validate a tag name argument (including group name and wildcards, etc)
  4492. # Inputs: 0) tag name
  4493. # Returns: true if tag name is valid
  4494. # - a tag name may contain [-_A-Za-z0-9], but may not start with [-0-9]
  4495. # - tag names may contain wildcards [?*], and end with a hash [#]
  4496. # - may have group name prefixes (which may have family number prefix), separated by colons
  4497. # - a group name may be zero or more characters
  4498. sub ValidTagName($)
  4499. {
  4500. my $tag = shift;
  4501. return $tag =~ /^(([-\w]*|\d*\*):)*[_a-zA-Z?*][-\w?*]*#?$/;
  4502. }
  4503. #------------------------------------------------------------------------------
  4504. # Generate a valid tag name based on the tag ID or name
  4505. # Inputs: 0) tag ID or name
  4506. # Returns: valid tag name
  4507. sub MakeTagName($)
  4508. {
  4509. my $name = shift;
  4510. $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
  4511. $name = ucfirst $name; # capitalize first letter
  4512. $name = "Tag$name" if length($name) < 2; # must at least 2 characters long
  4513. return $name;
  4514. }
  4515. #------------------------------------------------------------------------------
  4516. # Make description from a tag name
  4517. # Inputs: 0) tag name 1) optional tagID to add at end of description
  4518. # Returns: description
  4519. sub MakeDescription($;$)
  4520. {
  4521. my ($tag, $tagID) = @_;
  4522. # start with the tag name and force first letter to be upper case
  4523. my $desc = ucfirst($tag);
  4524. # translate underlines to spaces
  4525. $desc =~ tr/_/ /;
  4526. # remove hex TagID from name (to avoid inserting spaces in the number)
  4527. $desc =~ s/ (0x[\da-f]+)$//i and $tagID = $1 unless defined $tagID;
  4528. # put a space between lower/UPPER case and lower/number combinations
  4529. $desc =~ s/([a-z])([A-Z\d])/$1 $2/g;
  4530. # put a space between acronyms and words
  4531. $desc =~ s/([A-Z])([A-Z][a-z])/$1 $2/g;
  4532. # put spaces after numbers (if more than one character follows the number)
  4533. $desc =~ s/(\d)([A-Z]\S)/$1 $2/g;
  4534. # add TagID to description
  4535. $desc .= ' ' . $tagID if defined $tagID;
  4536. return $desc;
  4537. }
  4538. #------------------------------------------------------------------------------
  4539. # Get descriptions for all tags in an array
  4540. # Inputs: 0) ExifTool ref, 1) reference to list of tag keys
  4541. # Returns: reference to hash lookup for descriptions
  4542. # Note: Returned descriptions are NOT escaped by ESCAPE_PROC
  4543. sub GetDescriptions($$)
  4544. {
  4545. local $_;
  4546. my ($self, $tags) = @_;
  4547. my %desc;
  4548. my $oldEscape = $$self{ESCAPE_PROC};
  4549. delete $$self{ESCAPE_PROC};
  4550. $desc{$_} = $self->GetDescription($_) foreach @$tags;
  4551. $$self{ESCAPE_PROC} = $oldEscape;
  4552. return \%desc;
  4553. }
  4554. #------------------------------------------------------------------------------
  4555. # Apply filter to value(s) if necessary
  4556. # Inputs: 0) ExifTool ref, 1-N) references to values(s) to filter
  4557. # Returns: nothing, but changes values if necessary
  4558. sub Filter($@)
  4559. {
  4560. local $_;
  4561. my $self = shift;
  4562. return unless defined $$self{OPTIONS}{Filter};
  4563. while (@_) {
  4564. my $valPt = shift;
  4565. next unless defined $$valPt;
  4566. if (not ref $$valPt) {
  4567. $_ = $$valPt;
  4568. #### eval Filter ($_)
  4569. eval $$self{OPTIONS}{Filter};
  4570. $$valPt = $_;
  4571. } elsif (ref $$valPt eq 'SCALAR') {
  4572. my $val = $$$valPt; # make a copy to avoid filtering twice
  4573. $self->Filter(\$val);
  4574. $$valPt = \$val;
  4575. } elsif (ref $$valPt eq 'ARRAY') {
  4576. my @val = @{$$valPt}; # make a copy to avoid filtering twice
  4577. $self->Filter(\$_) foreach @val;
  4578. $$valPt = \@val;
  4579. } elsif (ref $$valPt eq 'HASH') {
  4580. my %val = %{$$valPt}; # make a copy to avoid filtering twice
  4581. $self->Filter(\$val{$_}) foreach keys %val;
  4582. $$valPt = \%val;
  4583. }
  4584. }
  4585. }
  4586. #------------------------------------------------------------------------------
  4587. # Return printable value
  4588. # Inputs: 0) ExifTool object reference
  4589. # 1) value to print, 2) line length limit (undef defaults to 60, 0=unlimited)
  4590. sub Printable($;$)
  4591. {
  4592. my ($self, $outStr, $maxLen) = @_;
  4593. return '(undef)' unless defined $outStr;
  4594. $outStr =~ tr/\x01-\x1f\x7f-\xff/./;
  4595. $outStr =~ s/\x00//g;
  4596. my $verbose = $$self{OPTIONS}{Verbose};
  4597. if ($verbose < 4) {
  4598. if ($maxLen) {
  4599. $maxLen = 20 if $maxLen < 20; # minimum length is 20
  4600. } elsif (defined $maxLen) {
  4601. $maxLen = length $outStr; # 0 is unlimited
  4602. } else {
  4603. $maxLen = 60; # default maximum is 60
  4604. }
  4605. } else {
  4606. $maxLen = length $outStr;
  4607. # limit to 2048 characters if verbose < 5
  4608. $maxLen = 2048 if $maxLen > 2048 and $verbose < 5;
  4609. }
  4610. # limit length if necessary
  4611. $outStr = substr($outStr,0,$maxLen-6) . '[snip]' if length($outStr) > $maxLen;
  4612. return $outStr;
  4613. }
  4614. #------------------------------------------------------------------------------
  4615. # Convert date/time from Exif format
  4616. # Inputs: 0) ExifTool object reference, 1) Date/time in EXIF format
  4617. # Returns: Formatted date/time string
  4618. sub ConvertDateTime($$)
  4619. {
  4620. my ($self, $date) = @_;
  4621. my $fmt = $$self{OPTIONS}{DateFormat};
  4622. my $shift = $$self{OPTIONS}{GlobalTimeShift};
  4623. if ($shift) {
  4624. my $dir = ($shift =~ s/^([-+])// and $1 eq '-') ? -1 : 1;
  4625. my $offset = $$self{GLOBAL_TIME_OFFSET};
  4626. $offset or $offset = $$self{GLOBAL_TIME_OFFSET} = { };
  4627. ShiftTime($date, $shift, $dir, $offset);
  4628. }
  4629. # only convert date if a format was specified and the date is recognizable
  4630. if ($fmt) {
  4631. # separate time zone if it exists
  4632. my $tz;
  4633. $date =~ s/([-+]\d{2}:\d{2}|Z)$// and $tz = $1;
  4634. # a few cameras use incorrect date/time formatting:
  4635. # - slashes instead of colons in date (RolleiD330, ImpressCam)
  4636. # - date/time values separated by colon instead of space (Polariod, Sanyo, Sharp, Vivitar)
  4637. # - single-digit seconds with leading space (HP scanners)
  4638. my @a = reverse ($date =~ /\d+/g); # be very flexible about date/time format
  4639. if (@a and $a[-1] >= 1000 and $a[-1] < 3000 and eval { require POSIX }) {
  4640. shift @a while @a > 6; # remove superfluous entries
  4641. unshift @a, 1 while @a < 3; # add month and day if necessary
  4642. unshift @a, 0 while @a < 6; # add h,m,s if necessary
  4643. $a[5] -= 1900; # base year is 1900
  4644. $a[4] -= 1; # base month is 1
  4645. # parse %z and %s ourself (to handle time zones properly)
  4646. if ($fmt =~ /%[sz]/) {
  4647. # use system time zone unless otherwise specified
  4648. $tz = TimeZoneString(\@a, TimeLocal(@a)) if not $tz and eval { require Time::Local };
  4649. # remove colon, setting to UTC if time zone is not numeric
  4650. $tz = ($tz and $tz=~/^([-+]\d{2}):(\d{2})$/) ? "$1$2" : '+0000';
  4651. $fmt =~ s/(^|[^%])((%%)*)%z/$1$2$tz/g; # convert '%z' format codes
  4652. if ($fmt =~ /%s/ and eval { require Time::Local }) {
  4653. # calculate seconds since the Epoch, UTC
  4654. my $s = Time::Local::timegm(@a) - 60 * ($tz - int($tz/100) * 40);
  4655. $fmt =~ s/(^|[^%])((%%)*)%s/$1$2$s/g; # convert '%s' format codes
  4656. }
  4657. }
  4658. $date = POSIX::strftime($fmt, @a); # generate the formatted date/time
  4659. } elsif ($$self{OPTIONS}{StrictDate}) {
  4660. undef $date;
  4661. }
  4662. }
  4663. return $date;
  4664. }
  4665. #------------------------------------------------------------------------------
  4666. # Print conversion for time span value
  4667. # Inputs: 0) time ticks, 1) number of seconds per tick (default 1)
  4668. # Returns: readable time
  4669. sub ConvertTimeSpan($;$)
  4670. {
  4671. my ($val, $mult) = @_;
  4672. if (Image::ExifTool::IsFloat($val) and $val != 0) {
  4673. $val *= $mult if $mult;
  4674. if ($val < 60) {
  4675. $val = "$val seconds";
  4676. } elsif ($val < 3600) {
  4677. my $fmt = ($mult and $mult >= 60) ? '%d' : '%.1f';
  4678. my $s = ($val == 60 and $mult) ? '' : 's';
  4679. $val = sprintf("$fmt minute$s", $val / 60);
  4680. } elsif ($val < 24 * 3600) {
  4681. $val = sprintf("%.1f hours", $val / 3600);
  4682. } else {
  4683. $val = sprintf("%.1f days", $val / (24 * 3600));
  4684. }
  4685. }
  4686. return $val;
  4687. }
  4688. #------------------------------------------------------------------------------
  4689. # Patched timelocal() that fixes ActivePerl timezone bug
  4690. # Inputs/Returns: same as timelocal()
  4691. # Notes: must 'require Time::Local' before calling this routine
  4692. sub TimeLocal(@)
  4693. {
  4694. my $tm = Time::Local::timelocal(@_);
  4695. if ($^O eq 'MSWin32') {
  4696. # patch for ActivePerl timezone bug
  4697. my @t2 = localtime($tm);
  4698. my $t2 = Time::Local::timelocal(@t2);
  4699. # adjust timelocal() return value to be consistent with localtime()
  4700. $tm += $tm - $t2;
  4701. }
  4702. return $tm;
  4703. }
  4704. #------------------------------------------------------------------------------
  4705. # Get time zone in minutes
  4706. # Inputs: 0) localtime array ref, 1) gmtime array ref
  4707. # Returns: time zone offset in minutes
  4708. sub GetTimeZone($$)
  4709. {
  4710. my ($tm, $gm) = @_;
  4711. # compute the number of minutes between localtime and gmtime
  4712. my $min = $$tm[2] * 60 + $$tm[1] - ($$gm[2] * 60 + $$gm[1]);
  4713. if ($$tm[3] != $$gm[3]) {
  4714. # account for case where one date wraps to the first of the next month
  4715. $$gm[3] = $$tm[3] - ($$tm[3]==1 ? 1 : -1) if abs($$tm[3]-$$gm[3]) != 1;
  4716. # adjust for the +/- one day difference
  4717. $min += ($$tm[3] - $$gm[3]) * 24 * 60;
  4718. }
  4719. # MirBSD patch to round to the nearest 30 minutes because
  4720. # it includes leap seconds in localtime but not gmtime
  4721. $min = int($min / 30 + ($min > 0 ? 0.5 : -0.5)) * 30 if $^O eq 'mirbsd';
  4722. return $min;
  4723. }
  4724. #------------------------------------------------------------------------------
  4725. # Get time zone string
  4726. # Inputs: 0) time zone offset in minutes
  4727. # or 0) localtime array ref, 1) corresponding time value
  4728. # Returns: time zone string ("+/-HH:MM")
  4729. sub TimeZoneString($;$)
  4730. {
  4731. my $min = shift;
  4732. if (ref $min) {
  4733. my @gm = gmtime(shift);
  4734. $min = GetTimeZone($min, \@gm);
  4735. }
  4736. my $sign = '+';
  4737. $min < 0 and $sign = '-', $min = -$min;
  4738. my $h = int($min / 60);
  4739. return sprintf('%s%.2d:%.2d', $sign, $h, $min - $h * 60);
  4740. }
  4741. #------------------------------------------------------------------------------
  4742. # Convert Unix time to EXIF date/time string
  4743. # Inputs: 0) Unix time value, 1) non-zero to convert to local time
  4744. # Returns: EXIF date/time string (with timezone for local times)
  4745. # Notes: fractional seconds are ignored
  4746. sub ConvertUnixTime($;$)
  4747. {
  4748. my ($time, $toLocal) = @_;
  4749. return '0000:00:00 00:00:00' if $time == 0;
  4750. $time = int($time + 1e-6) if $time != int($time); # avoid round-off errors
  4751. my (@tm, $tz);
  4752. if ($toLocal) {
  4753. @tm = localtime($time);
  4754. $tz = TimeZoneString(\@tm, $time);
  4755. } else {
  4756. @tm = gmtime($time);
  4757. $tz = '';
  4758. }
  4759. my $str = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d%s",
  4760. $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $tz);
  4761. return $str;
  4762. }
  4763. #------------------------------------------------------------------------------
  4764. # Get Unix time from EXIF-formatted date/time string with optional timezone
  4765. # Inputs: 0) EXIF date/time string, 1) non-zero if time is local
  4766. # Returns: Unix time (seconds since 0:00 GMT Jan 1, 1970) or undefined on error
  4767. sub GetUnixTime($;$)
  4768. {
  4769. my ($timeStr, $isLocal) = @_;
  4770. return 0 if $timeStr eq '0000:00:00 00:00:00';
  4771. my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/);
  4772. return undef unless @tm == 6 and eval { require Time::Local };
  4773. my $tzsec = 0;
  4774. # use specified timezone offset (if given) instead of local system time
  4775. # if we are converting a local time value
  4776. if ($isLocal and $timeStr =~ /(?:Z|([-+])(\d+):(\d+))$/i) {
  4777. # use specified timezone if one exists
  4778. $tzsec = ($2 * 60 + $3) * ($1 eq '-' ? -60 : 60) if $1;
  4779. undef $isLocal; # convert using GMT corrected for specified timezone
  4780. }
  4781. $tm[0] -= 1900; # convert year
  4782. $tm[1] -= 1; # convert month
  4783. @tm = reverse @tm; # change to order required by timelocal()
  4784. return $isLocal ? TimeLocal(@tm) : Time::Local::timegm(@tm) - $tzsec;
  4785. }
  4786. #------------------------------------------------------------------------------
  4787. # Print conversion for file size
  4788. # Inputs: 0) file size in bytes
  4789. # Returns: converted file size
  4790. sub ConvertFileSize($)
  4791. {
  4792. my $val = shift;
  4793. $val < 2048 and return "$val bytes";
  4794. $val < 10240 and return sprintf('%.1f kB', $val / 1024);
  4795. $val < 2097152 and return sprintf('%.0f kB', $val / 1024);
  4796. $val < 10485760 and return sprintf('%.1f MB', $val / 1048576);
  4797. return sprintf('%.0f MB', $val / 1048576);
  4798. }
  4799. #------------------------------------------------------------------------------
  4800. # Convert seconds to duration string (handles negative durations)
  4801. # Inputs: 0) floating point seconds
  4802. # Returns: duration string in form "S.SS s", "MM:SS" or "H:MM:SS"
  4803. sub ConvertDuration($)
  4804. {
  4805. my $time = shift;
  4806. return $time unless IsFloat($time);
  4807. return '0 s' if $time == 0;
  4808. my $sign = ($time > 0 ? '' : (($time = -$time), '-'));
  4809. return sprintf("$sign%.2f s", $time) if $time < 30;
  4810. my $h = int($time / 3600);
  4811. $time -= $h * 3600;
  4812. my $m = int($time / 60);
  4813. $time -= $m * 60;
  4814. if ($h > 24) {
  4815. my $d = int($h / 24);
  4816. $h -= $d * 24;
  4817. $sign = "$sign$d days ";
  4818. }
  4819. return sprintf("$sign%d:%.2d:%.2d", $h, $m, int($time));
  4820. }
  4821. #------------------------------------------------------------------------------
  4822. # Print conversion for bitrate values
  4823. # Inputs: 0) bitrate in bits per second
  4824. # Returns: human-readable bitrate string
  4825. # Notes: returns input value without formatting if it isn't numerical
  4826. sub ConvertBitrate($)
  4827. {
  4828. my $bitrate = shift;
  4829. IsFloat($bitrate) or return $bitrate;
  4830. my @units = ('bps', 'kbps', 'Mbps', 'Gbps');
  4831. for (;;) {
  4832. my $units = shift @units;
  4833. $bitrate >= 1000 and @units and $bitrate /= 1000, next;
  4834. my $fmt = $bitrate < 100 ? '%.3g' : '%.0f';
  4835. return sprintf("$fmt $units", $bitrate);
  4836. }
  4837. }
  4838. #------------------------------------------------------------------------------
  4839. # Convert file name for printing
  4840. # Inputs: 0) ExifTool ref, 1) file name in CharsetFileName character set
  4841. # Returns: converted file name in external character set
  4842. sub ConvertFileName($$)
  4843. {
  4844. my ($self, $val) = @_;
  4845. my $enc = $$self{OPTIONS}{CharsetFileName};
  4846. $val = $self->Decode($val, $enc) if $enc;
  4847. return $val;
  4848. }
  4849. #------------------------------------------------------------------------------
  4850. # Inverse conversion for file name (encode
  4851. # Inputs: 0) ExifTool ref, 1) file name in external character set
  4852. # Returns: file name in CharsetFileName character set
  4853. sub InverseFileName($$)
  4854. {
  4855. my ($self, $val) = @_;
  4856. my $enc = $$self{OPTIONS}{CharsetFileName};
  4857. $val = $self->Encode($val, $enc) if $enc;
  4858. $val =~ tr/\\/\//; # make sure we are using forward slashes
  4859. return $val;
  4860. }
  4861. #------------------------------------------------------------------------------
  4862. # Save information for HTML dump
  4863. # Inputs: 0) ExifTool hash ref, 1) start offset, 2) data size
  4864. # 3) comment string, 4) tool tip (or SAME), 5) flags
  4865. sub HDump($$$$;$$)
  4866. {
  4867. my $self = shift;
  4868. if ($$self{HTML_DUMP}) {
  4869. my $pos = shift;
  4870. $pos += $$self{BASE} if $$self{BASE};
  4871. $$self{HTML_DUMP}->Add($pos, @_);
  4872. }
  4873. }
  4874. #------------------------------------------------------------------------------
  4875. # Identify trailer ending at specified offset from end of file
  4876. # Inputs: 0) RAF reference, 1) offset from end of file (0 by default)
  4877. # Returns: Trailer info hash (with RAF and DirName set),
  4878. # or undef if no recognized trailer was found
  4879. # Notes: leaves file position unchanged
  4880. sub IdentifyTrailer($;$)
  4881. {
  4882. my $raf = shift;
  4883. my $offset = shift || 0;
  4884. my $pos = $raf->Tell();
  4885. my ($buff, $type, $len);
  4886. while ($raf->Seek(-$offset, 2) and ($len = $raf->Tell()) > 0) {
  4887. # read up to 64 bytes before specified offset from end of file
  4888. $len = 64 if $len > 64;
  4889. $raf->Seek(-$len, 1) and $raf->Read($buff, $len) == $len or last;
  4890. if ($buff =~ /AXS(!|\*).{8}$/s) {
  4891. $type = 'AFCP';
  4892. } elsif ($buff =~ /\xa1\xb2\xc3\xd4$/) {
  4893. $type = 'FotoStation';
  4894. } elsif ($buff =~ /cbipcbbl$/) {
  4895. $type = 'PhotoMechanic';
  4896. } elsif ($buff =~ /^CANON OPTIONAL DATA\0/) {
  4897. $type = 'CanonVRD';
  4898. } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}[\x10\x18]\x04$/s or
  4899. $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}[\x10\x18]\x08$/s)
  4900. {
  4901. $type = 'MIE';
  4902. } elsif ($buff =~ /\0\0(QDIOBS|SEFT)$/) {
  4903. $type = 'Samsung';
  4904. }
  4905. last;
  4906. }
  4907. $raf->Seek($pos, 0); # restore original file position
  4908. return $type ? { RAF => $raf, DirName => $type } : undef;
  4909. }
  4910. #------------------------------------------------------------------------------
  4911. # Read/rewrite trailer information (including multiple trailers)
  4912. # Inputs: 0) ExifTool object ref, 1) DirInfo ref:
  4913. # - requires RAF and DirName
  4914. # - OutFile is a scalar reference for writing
  4915. # - scans from current file position if ScanForAFCP is set
  4916. # Returns: 1 if trailer was processed or couldn't be processed (or written OK)
  4917. # 0 if trailer was recognized but offsets need fixing (or write error)
  4918. # - DirName, DirLen, DataPos, Offset, Fixup and OutFile are updated
  4919. # - preserves current file position and byte order
  4920. sub ProcessTrailers($$)
  4921. {
  4922. my ($self, $dirInfo) = @_;
  4923. my $dirName = $$dirInfo{DirName};
  4924. my $outfile = $$dirInfo{OutFile};
  4925. my $offset = $$dirInfo{Offset} || 0;
  4926. my $fixup = $$dirInfo{Fixup};
  4927. my $raf = $$dirInfo{RAF};
  4928. my $pos = $raf->Tell();
  4929. my $byteOrder = GetByteOrder();
  4930. my $success = 1;
  4931. my $path = $$self{PATH};
  4932. for (;;) { # loop through all trailers
  4933. require "Image/ExifTool/$dirName.pm";
  4934. my $proc = "Image::ExifTool::${dirName}::Process$dirName";
  4935. my $outBuff;
  4936. if ($outfile) {
  4937. # write to local buffer so we can add trailer in proper order later
  4938. $$outfile and $$dirInfo{OutFile} = \$outBuff, $outBuff = '';
  4939. # must generate new fixup if necessary so we can shift
  4940. # the old fixup separately after we prepend this trailer
  4941. delete $$dirInfo{Fixup};
  4942. }
  4943. delete $$dirInfo{DirLen}; # reset trailer length
  4944. $$dirInfo{Offset} = $offset; # set offset from end of file
  4945. $$dirInfo{Trailer} = 1; # set Trailer flag in case proc cares
  4946. # add trailer and DirName to SubDirectory PATH
  4947. push @$path, 'Trailer', $dirName;
  4948. # read or write this trailer
  4949. # (proc takes Offset as offset from end of trailer to end of file,
  4950. # and returns DataPos and DirLen, and Fixup if applicable)
  4951. no strict 'refs';
  4952. my $result = &$proc($self, $dirInfo);
  4953. use strict 'refs';
  4954. # restore PATH (pop last 2 items)
  4955. splice @$path, -2;
  4956. # check result
  4957. if ($outfile) {
  4958. if ($result > 0) {
  4959. if ($outBuff) {
  4960. # write trailers to OutFile in original order
  4961. $$outfile = $outBuff . $$outfile;
  4962. # must adjust old fixup start if it exists
  4963. $$fixup{Start} += length($outBuff) if $fixup;
  4964. $outBuff = ''; # free memory
  4965. }
  4966. if ($$dirInfo{Fixup}) {
  4967. if ($fixup) {
  4968. # add fixup for subsequent trailers to the fixup for this trailer
  4969. # (but first we must adjust for the new start position)
  4970. $$fixup{Shift} += $$dirInfo{Fixup}{Start};
  4971. $$fixup{Start} -= $$dirInfo{Fixup}{Start};
  4972. $$dirInfo{Fixup}->AddFixup($fixup);
  4973. }
  4974. $fixup = $$dirInfo{Fixup}; # save fixup
  4975. }
  4976. } else {
  4977. $success = 0 if $self->Error("Error rewriting $dirName trailer", 2);
  4978. last;
  4979. }
  4980. } elsif ($result < 0) {
  4981. # can't continue if we must scan for this trailer
  4982. $success = 0;
  4983. last;
  4984. }
  4985. last unless $result > 0 and $$dirInfo{DirLen};
  4986. # look for next trailer
  4987. $offset += $$dirInfo{DirLen};
  4988. my $nextTrail = IdentifyTrailer($raf, $offset) or last;
  4989. $dirName = $$dirInfo{DirName} = $$nextTrail{DirName};
  4990. $raf->Seek($pos, 0);
  4991. }
  4992. SetByteOrder($byteOrder); # restore original byte order
  4993. $raf->Seek($pos, 0); # restore original file position
  4994. $$dirInfo{OutFile} = $outfile; # restore original outfile
  4995. $$dirInfo{Offset} = $offset; # return offset from EOF to start of first trailer
  4996. $$dirInfo{Fixup} = $fixup; # return fixup information
  4997. return $success;
  4998. }
  4999. #------------------------------------------------------------------------------
  5000. # JPEG constants
  5001. # JPEG marker names
  5002. %jpegMarker = (
  5003. 0x00 => 'NULL',
  5004. 0x01 => 'TEM',
  5005. 0xc0 => 'SOF0', # to SOF15, with a few exceptions below
  5006. 0xc4 => 'DHT',
  5007. 0xc8 => 'JPGA',
  5008. 0xcc => 'DAC',
  5009. 0xd0 => 'RST0',
  5010. 0xd8 => 'SOI',
  5011. 0xd9 => 'EOI',
  5012. 0xda => 'SOS',
  5013. 0xdb => 'DQT',
  5014. 0xdc => 'DNL',
  5015. 0xdd => 'DRI',
  5016. 0xde => 'DHP',
  5017. 0xdf => 'EXP',
  5018. 0xe0 => 'APP0', # to APP15
  5019. 0xf0 => 'JPG0',
  5020. 0xfe => 'COM',
  5021. );
  5022. # lookup for size of JPEG marker length word
  5023. # (2 bytes assumed unless specified here)
  5024. my %markerLenBytes = (
  5025. 0x00 => 0, 0x01 => 0,
  5026. 0xd0 => 0, 0xd1 => 0, 0xd2 => 0, 0xd3 => 0, 0xd4 => 0, 0xd5 => 0, 0xd6 => 0, 0xd7 => 0,
  5027. 0xd8 => 0, 0xd9 => 0, 0xda => 0,
  5028. # J2C
  5029. 0x30 => 0, 0x31 => 0, 0x32 => 0, 0x33 => 0, 0x34 => 0, 0x35 => 0, 0x36 => 0, 0x37 => 0,
  5030. 0x38 => 0, 0x39 => 0, 0x3a => 0, 0x3b => 0, 0x3c => 0, 0x3d => 0, 0x3e => 0, 0x3f => 0,
  5031. 0x4f => 0,
  5032. 0x92 => 0, 0x93 => 0,
  5033. # J2C extensions
  5034. 0x74 => 4, 0x75 => 4, 0x77 => 4,
  5035. );
  5036. #------------------------------------------------------------------------------
  5037. # Get JPEG marker name
  5038. # Inputs: 0) Jpeg number
  5039. # Returns: marker name
  5040. sub JpegMarkerName($)
  5041. {
  5042. my $marker = shift;
  5043. my $markerName = $jpegMarker{$marker};
  5044. unless ($markerName) {
  5045. $markerName = $jpegMarker{$marker & 0xf0};
  5046. if ($markerName and $markerName =~ /^([A-Z]+)\d+$/) {
  5047. $markerName = $1 . ($marker & 0x0f);
  5048. } else {
  5049. $markerName = sprintf("marker 0x%.2x", $marker);
  5050. }
  5051. }
  5052. return $markerName;
  5053. }
  5054. #------------------------------------------------------------------------------
  5055. # Adjust directory start position
  5056. # Inputs: 0) dirInfo ref, 1) start offset
  5057. # 2) Base for offsets (relative to DataPos, defaults to absolute Base of 0)
  5058. sub DirStart($$;$)
  5059. {
  5060. my ($dirInfo, $start, $base) = @_;
  5061. $$dirInfo{DirStart} = $start;
  5062. $$dirInfo{DirLen} -= $start;
  5063. if (defined $base) {
  5064. $$dirInfo{Base} = $$dirInfo{DataPos} + $base;
  5065. $$dirInfo{DataPos} = -$base; # (relative to Base!)
  5066. }
  5067. }
  5068. #------------------------------------------------------------------------------
  5069. # Extract metadata from a jpg image
  5070. # Inputs: 0) ExifTool object reference, 1) dirInfo ref with RAF set
  5071. # Returns: 1 on success, 0 if this wasn't a valid JPEG file
  5072. sub ProcessJPEG($$)
  5073. {
  5074. local $_;
  5075. my ($self, $dirInfo) = @_;
  5076. my ($ch, $s, $length);
  5077. my $verbose = $$self{OPTIONS}{Verbose};
  5078. my $out = $$self{OPTIONS}{TextOut};
  5079. my $fast = $$self{OPTIONS}{FastScan};
  5080. my $raf = $$dirInfo{RAF};
  5081. my $htmlDump = $$self{HTML_DUMP};
  5082. my %dumpParms = ( Out => $out );
  5083. my ($success, $wantTrailer, $trailInfo, $type);
  5084. my (@iccChunk, $iccChunkCount, $iccChunksTotal, @flirChunk, $flirCount, $flirTotal);
  5085. my ($preview, $scalado, @dqt, $subSampling, $dumpEnd, %extendedXMP);
  5086. # check to be sure this is a valid JPG (or J2C, or EXV) file
  5087. return 0 unless $raf->Read($s, 2) == 2 and $s =~ /^\xff[\xd8\x4f\x01]/;
  5088. if ($s eq "\xff\x01") {
  5089. return 0 unless $raf->Read($s, 5) == 5 and $s eq 'Exiv2';
  5090. $type = 'EXV';
  5091. }
  5092. if (not $$self{VALUE}{FileType} or ($$self{DOC_NUM} and $$self{OPTIONS}{ExtractEmbedded})) {
  5093. $self->SetFileType($type); # set FileType tag
  5094. return 1 if $fast and $fast == 3; # don't process file when FastScan == 3
  5095. $$self{LOW_PRIORITY_DIR}{IFD1} = 1; # lower priority of IFD1 tags
  5096. }
  5097. $dumpParms{MaxLen} = 128 if $verbose < 4;
  5098. if ($htmlDump) {
  5099. $dumpEnd = $raf->Tell();
  5100. my ($n, $t, $m) = $s eq 'Exiv2' ? (7,'EXV','TEM') : (2,'JPEG','SOI');
  5101. my $pos = $dumpEnd - $n;
  5102. $self->HDump(0, $pos, '[unknown header]') if $pos;
  5103. $self->HDump($pos, $n, "$t header", "$m Marker");
  5104. }
  5105. my $path = $$self{PATH};
  5106. my $pn = scalar @$path;
  5107. # set input record separator to 0xff (the JPEG marker) to make reading quicker
  5108. local $/ = "\xff";
  5109. my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData);
  5110. # read file until we reach an end of image (EOI) or start of scan (SOS)
  5111. Marker: for (;;) {
  5112. # set marker and data pointer for current segment
  5113. my $marker = $nextMarker;
  5114. my $segDataPt = $nextSegDataPt;
  5115. my $segPos = $nextSegPos;
  5116. undef $nextMarker;
  5117. undef $nextSegDataPt;
  5118. #
  5119. # read ahead to the next segment unless we have reached EOI, SOS or SOD
  5120. #
  5121. unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantTrailer) or $marker==0x93)) {
  5122. # read up to next marker (JPEG markers begin with 0xff)
  5123. my $buff;
  5124. $raf->ReadLine($buff) or last;
  5125. # JPEG markers can be padded with unlimited 0xff's
  5126. for (;;) {
  5127. $raf->Read($ch, 1) or last Marker;
  5128. $nextMarker = ord($ch);
  5129. last unless $nextMarker == 0xff;
  5130. }
  5131. # read segment data if it exists
  5132. if (not defined $markerLenBytes{$nextMarker}) {
  5133. # read record length word
  5134. last unless $raf->Read($s, 2) == 2;
  5135. my $len = unpack('n',$s); # get data length
  5136. last unless defined($len) and $len >= 2;
  5137. $nextSegPos = $raf->Tell();
  5138. $len -= 2; # subtract size of length word
  5139. last unless $raf->Read($buff, $len) == $len;
  5140. $nextSegDataPt = \$buff; # set pointer to our next data
  5141. } elsif ($markerLenBytes{$nextMarker} == 4) {
  5142. # handle J2C extensions with 4-byte length word
  5143. last unless $raf->Read($s, 4) == 4;
  5144. my $len = unpack('N',$s); # get data length
  5145. last unless defined($len) and $len >= 4;
  5146. $nextSegPos = $raf->Tell();
  5147. $len -= 4; # subtract size of length word
  5148. last unless $raf->Seek($len, 1);
  5149. }
  5150. # read second segment too if this was the first
  5151. next unless defined $marker;
  5152. }
  5153. # set some useful variables for the current segment
  5154. my $markerName = JpegMarkerName($marker);
  5155. $$path[$pn] = $markerName;
  5156. #
  5157. # parse the current segment
  5158. #
  5159. # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
  5160. if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
  5161. $length = length $$segDataPt;
  5162. if ($verbose) {
  5163. print $out "JPEG $markerName ($length bytes):\n";
  5164. HexDump($segDataPt, undef, %dumpParms, Addr=>$segPos) if $verbose>2;
  5165. } elsif ($htmlDump) {
  5166. $self->HDump($segPos-4, $length+4, "[JPEG $markerName]", undef, 0x08);
  5167. $dumpEnd = $segPos + $length;
  5168. }
  5169. next unless $length >= 6;
  5170. # extract some useful information
  5171. my ($p, $h, $w, $n) = unpack('Cn2C', $$segDataPt);
  5172. my $sof = GetTagTable('Image::ExifTool::JPEG::SOF');
  5173. $self->HandleTag($sof, 'ImageWidth', $w);
  5174. $self->HandleTag($sof, 'ImageHeight', $h);
  5175. $self->HandleTag($sof, 'EncodingProcess', $marker - 0xc0);
  5176. $self->HandleTag($sof, 'BitsPerSample', $p);
  5177. $self->HandleTag($sof, 'ColorComponents', $n);
  5178. next unless $n == 3 and $length >= 15;
  5179. my ($i, $hmin, $hmax, $vmin, $vmax);
  5180. # loop through all components to determine sampling frequency
  5181. $subSampling = '';
  5182. for ($i=0; $i<$n; ++$i) {
  5183. my $sf = Get8u($segDataPt, 7 + 3 * $i);
  5184. $subSampling .= sprintf('%.2x', $sf);
  5185. # isolate horizontal and vertical components
  5186. my ($hf, $vf) = ($sf >> 4, $sf & 0x0f);
  5187. unless ($i) {
  5188. $hmin = $hmax = $hf;
  5189. $vmin = $vmax = $vf;
  5190. next;
  5191. }
  5192. # determine min/max frequencies
  5193. $hmin = $hf if $hf < $hmin;
  5194. $hmax = $hf if $hf > $hmax;
  5195. $vmin = $vf if $vf < $vmin;
  5196. $vmax = $vf if $vf > $vmax;
  5197. }
  5198. if ($hmin and $vmin) {
  5199. my ($hs, $vs) = ($hmax / $hmin, $vmax / $vmin);
  5200. $self->FoundTag($$sof{YCbCrSubSampling}, "$hs $vs");
  5201. }
  5202. next;
  5203. } elsif ($marker == 0xd9) { # EOI
  5204. pop @$path;
  5205. $verbose and print $out "JPEG EOI\n";
  5206. my $pos = $raf->Tell();
  5207. if ($htmlDump and $dumpEnd) {
  5208. $self->HDump($dumpEnd, $pos-2-$dumpEnd, '[JPEG Image Data]', undef, 0x08);
  5209. $self->HDump($pos-2, 2, 'JPEG EOI', undef);
  5210. $dumpEnd = 0;
  5211. }
  5212. $success = 1;
  5213. # we are here because we are looking for trailer information
  5214. if ($wantTrailer) {
  5215. my $start = $$self{PreviewImageStart};
  5216. if ($start) {
  5217. my $buff;
  5218. # most previews start right after the JPEG EOI, but the Olympus E-20
  5219. # preview is 508 bytes into the trailer, the K-M Maxxum 7D preview is
  5220. # 979 bytes in, and Sony previews can start up to 32 kB into the trailer.
  5221. # (and Minolta and Sony previews can have a random first byte...)
  5222. my $scanLen = $$self{Make} =~ /Sony/i ? 65536 : 1024;
  5223. if ($raf->Read($buff, $scanLen) and ($buff =~ /\xff\xd8\xff./g or
  5224. ($$self{Make} =~ /(Minolta|Sony)/i and $buff =~ /.\xd8\xff\xdb/g)))
  5225. {
  5226. # adjust PreviewImageStart to this location
  5227. my $actual = $pos + pos($buff) - 4;
  5228. if ($start ne $actual and $verbose > 1) {
  5229. print $out "(Fixed PreviewImage location: $start -> $actual)\n";
  5230. }
  5231. # update preview image offsets
  5232. $$self{VALUE}{PreviewImageStart} = $actual if $$self{VALUE}{PreviewImageStart};
  5233. $$self{PreviewImageStart} = $actual;
  5234. # load preview now if we tried and failed earlier
  5235. if ($$self{PreviewError} and $$self{PreviewImageLength}) {
  5236. if ($raf->Seek($actual, 0) and $raf->Read($buff, $$self{PreviewImageLength})) {
  5237. $self->FoundTag('PreviewImage', $buff);
  5238. delete $$self{PreviewError};
  5239. }
  5240. }
  5241. }
  5242. $raf->Seek($pos, 0);
  5243. }
  5244. }
  5245. # process trailer now or finish processing trailers
  5246. # and scan for AFCP if necessary
  5247. my $fromEnd = 0;
  5248. if ($trailInfo) {
  5249. $$trailInfo{ScanForAFCP} = 1; # scan now if necessary
  5250. $self->ProcessTrailers($trailInfo);
  5251. # save offset from end of file to start of first trailer
  5252. $fromEnd = $$trailInfo{Offset};
  5253. undef $trailInfo;
  5254. }
  5255. if ($$self{LeicaTrailer}) {
  5256. $raf->Seek(0, 2);
  5257. $$self{LeicaTrailer}{TrailPos} = $pos;
  5258. $$self{LeicaTrailer}{TrailLen} = $raf->Tell() - $pos - $fromEnd;
  5259. Image::ExifTool::Panasonic::ProcessLeicaTrailer($self);
  5260. }
  5261. # finally, dump remaining information in JPEG trailer
  5262. if ($verbose or $htmlDump) {
  5263. my $endPos = $$self{LeicaTrailerPos};
  5264. unless ($endPos) {
  5265. $raf->Seek(0, 2);
  5266. $endPos = $raf->Tell() - $fromEnd;
  5267. }
  5268. $self->DumpUnknownTrailer({
  5269. RAF => $raf,
  5270. DataPos => $pos,
  5271. DirLen => $endPos - $pos
  5272. }) if $endPos > $pos;
  5273. }
  5274. last; # all done parsing file
  5275. } elsif ($marker == 0xda) { # SOS
  5276. pop @$path;
  5277. # all done with meta information unless we have a trailer
  5278. $verbose and print $out "JPEG SOS\n";
  5279. unless ($fast) {
  5280. $trailInfo = IdentifyTrailer($raf);
  5281. # process trailer now unless we are doing verbose dump
  5282. if ($trailInfo and $verbose < 3 and not $htmlDump) {
  5283. # process trailers (keep trailInfo to finish processing later
  5284. # only if we can't finish without scanning from end of file)
  5285. $self->ProcessTrailers($trailInfo) and undef $trailInfo;
  5286. }
  5287. if ($wantTrailer) {
  5288. # seek ahead and validate preview image
  5289. my $buff;
  5290. my $curPos = $raf->Tell();
  5291. if ($raf->Seek($$self{PreviewImageStart}, 0) and
  5292. $raf->Read($buff, 4) == 4 and
  5293. $buff =~ /^.\xd8\xff[\xc4\xdb\xe0-\xef]/)
  5294. {
  5295. undef $wantTrailer;
  5296. }
  5297. $raf->Seek($curPos, 0) or last;
  5298. }
  5299. # seek ahead and process Leica trailer
  5300. if ($$self{LeicaTrailer}) {
  5301. require Image::ExifTool::Panasonic;
  5302. Image::ExifTool::Panasonic::ProcessLeicaTrailer($self);
  5303. $wantTrailer = 1 if $$self{LeicaTrailer};
  5304. }
  5305. next if $trailInfo or $wantTrailer or $verbose > 2 or $htmlDump;
  5306. }
  5307. # nothing interesting to parse after start of scan (SOS)
  5308. $success = 1;
  5309. last; # all done parsing file
  5310. } elsif ($marker == 0x93) {
  5311. pop @$path;
  5312. $verbose and print $out "JPEG SOD\n";
  5313. $success = 1;
  5314. next if $verbose > 2 or $htmlDump;
  5315. last; # all done parsing file
  5316. } elsif (defined $markerLenBytes{$marker}) {
  5317. # handle other stand-alone markers and segments we skipped over
  5318. $verbose and $marker and print $out "JPEG $markerName\n";
  5319. next;
  5320. } elsif ($marker == 0xdb and length($$segDataPt) and # DQT
  5321. # save the DQT data only if JPEGDigest has been requested
  5322. # (Note: this will not work with the application -p option
  5323. # because we aren't checking the RequestAll API option here.
  5324. # The reason is that there is too much overhead involved in
  5325. # the calculation of this tag to make this worth the CPU time.)
  5326. ($$self{REQ_TAG_LOOKUP}{jpegdigest} or $$self{REQ_TAG_LOOKUP}{jpegqualityestimate}))
  5327. {
  5328. my $num = unpack('C',$$segDataPt) & 0x0f; # get table index
  5329. $dqt[$num] = $$segDataPt if $num < 4; # save for MD5 calculation
  5330. }
  5331. # handle all other markers
  5332. my $dumpType = '';
  5333. my ($desc, $tip);
  5334. $length = length $$segDataPt;
  5335. if ($verbose) {
  5336. print $out "JPEG $markerName ($length bytes):\n";
  5337. if ($verbose > 2) {
  5338. my %extraParms = ( Addr => $segPos );
  5339. $extraParms{MaxLen} = 128 if $verbose == 4;
  5340. HexDump($segDataPt, undef, %dumpParms, %extraParms);
  5341. }
  5342. }
  5343. # prepare dirInfo hash for processing this information
  5344. my %dirInfo = (
  5345. Parent => $markerName,
  5346. DataPt => $segDataPt,
  5347. DataPos => $segPos,
  5348. DataLen => $length,
  5349. DirStart => 0,
  5350. DirLen => $length,
  5351. Base => 0,
  5352. );
  5353. if ($marker == 0xe0) { # APP0 (JFIF, JFXX, CIFF, AVI1, Ocad)
  5354. if ($$segDataPt =~ /^JFIF\0/) {
  5355. $dumpType = 'JFIF';
  5356. DirStart(\%dirInfo, 5); # start at byte 5
  5357. SetByteOrder('MM');
  5358. my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
  5359. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5360. } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
  5361. $dumpType = 'JFXX';
  5362. my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Extension');
  5363. my $tagInfo = $self->GetTagInfo($tagTablePtr, 0x10);
  5364. $self->FoundTag($tagInfo, substr($$segDataPt, 6));
  5365. } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
  5366. next if $fast and $fast > 1; # skip processing for very fast
  5367. $dumpType = 'CIFF';
  5368. my %dirInfo = ( RAF => new File::RandomAccess($segDataPt) );
  5369. $$self{SET_GROUP1} = 'CIFF';
  5370. push @{$$self{PATH}}, 'CIFF';
  5371. require Image::ExifTool::CanonRaw;
  5372. Image::ExifTool::CanonRaw::ProcessCRW($self, \%dirInfo);
  5373. pop @{$$self{PATH}};
  5374. delete $$self{SET_GROUP1};
  5375. } elsif ($$segDataPt =~ /^(AVI1|Ocad)/) {
  5376. $dumpType = $1;
  5377. SetByteOrder('MM');
  5378. my $tagTablePtr = GetTagTable("Image::ExifTool::JPEG::$dumpType");
  5379. DirStart(\%dirInfo, 4);
  5380. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5381. }
  5382. } elsif ($marker == 0xe1) { # APP1 (EXIF, XMP, QVCI)
  5383. # (some Kodak cameras don't put a second "\0", and I have seen an
  5384. # example where there was a second 4-byte APP1 segment header)
  5385. if ($$segDataPt =~ /^(.{0,4})Exif\0/is) {
  5386. undef $dumpType; # (will be dumped here)
  5387. # this is EXIF data --
  5388. # get the data block (into a common variable)
  5389. my $hdrLen = length($exifAPP1hdr);
  5390. if (length $1) {
  5391. $hdrLen += length $1;
  5392. $self->Warn('Unknown garbage at start of EXIF segment',1);
  5393. } elsif ($$segDataPt !~ /^Exif\0/) {
  5394. $self->Warn('Incorrect EXIF segment identifier',1);
  5395. }
  5396. DirStart(\%dirInfo, $hdrLen, $hdrLen);
  5397. if ($htmlDump) {
  5398. $self->HDump($segPos-4, 4, 'APP1 header', "Data size: $length bytes");
  5399. $self->HDump($segPos, $hdrLen, 'Exif header', 'APP1 data type: Exif');
  5400. $dumpEnd = $segPos + $length;
  5401. }
  5402. # extract the EXIF information (it is in standard TIFF format)
  5403. $self->ProcessTIFF(\%dirInfo);
  5404. # avoid looking for preview unless necessary because it really slows
  5405. # us down -- only look for it if we found pointer, and preview is
  5406. # outside EXIF, and PreviewImage is specifically requested
  5407. my $start = $self->GetValue('PreviewImageStart', 'ValueConv');
  5408. my $plen = $self->GetValue('PreviewImageLength', 'ValueConv');
  5409. if (not $start or not $plen and $$self{PreviewError}) {
  5410. $start = $$self{PreviewImageStart};
  5411. $plen = $$self{PreviewImageLength};
  5412. }
  5413. if ($start and $plen and IsInt($start) and IsInt($plen) and
  5414. $start + $plen > $$self{EXIF_POS} + length($$self{EXIF_DATA}) and
  5415. ($$self{REQ_TAG_LOOKUP}{previewimage} or
  5416. # (extracted normally, so check Binary option)
  5417. ($$self{OPTIONS}{Binary} and not $$self{EXCL_TAG_LOOKUP}{previewimage})))
  5418. {
  5419. $$self{PreviewImageStart} = $start;
  5420. $$self{PreviewImageLength} = $plen;
  5421. $wantTrailer = 1;
  5422. }
  5423. } elsif ($$segDataPt =~ /^$xmpExtAPP1hdr/) {
  5424. # off len -- extended XMP header (75 bytes total):
  5425. # 0 35 bytes - signature
  5426. # 35 32 bytes - GUID (MD5 hash of full extended XMP data in ASCII)
  5427. # 67 4 bytes - total size of extended XMP data
  5428. # 71 4 bytes - offset for this XMP data portion
  5429. $dumpType = 'Extended XMP';
  5430. if ($length > 75) {
  5431. my ($size, $off) = unpack('x67N2', $$segDataPt);
  5432. my $guid = substr($$segDataPt, 35, 32);
  5433. if ($guid =~ /[^A-Za-z0-9]/) { # (technically, should be uppercase)
  5434. $self->WarnOnce($tip = 'Invalid extended XMP GUID');
  5435. } else {
  5436. my $extXMP = $extendedXMP{$guid};
  5437. if (not $extXMP) {
  5438. $extXMP = $extendedXMP{$guid} = { };
  5439. } elsif ($size != $$extXMP{Size}) {
  5440. $self->WarnOnce('Inconsistent extended XMP size');
  5441. }
  5442. $$extXMP{Size} = $size;
  5443. $$extXMP{$off} = substr($$segDataPt, 75);
  5444. $tip = "Full length: $size\nChunk offset: $off\nChunk length: " .
  5445. ($length - 75) . "\nGUID: $guid";
  5446. # (delay processing extended XMP until after reading all segments)
  5447. }
  5448. } else {
  5449. $self->WarnOnce($tip = 'Invalid extended XMP segment');
  5450. }
  5451. } elsif ($$segDataPt =~ /^QVCI\0/) {
  5452. $dumpType = 'QVCI';
  5453. my $tagTablePtr = GetTagTable('Image::ExifTool::Casio::QVCI');
  5454. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5455. } elsif ($$segDataPt =~ /^FLIR\0/ and $length >= 8) {
  5456. $dumpType = 'FLIR';
  5457. # must concatenate FLIR chunks (note: handle the case where
  5458. # some software erroneously writes zeros for the chunk counts)
  5459. my $chunkNum = Get8u($segDataPt, 6);
  5460. my $chunksTot = Get8u($segDataPt, 7) + 1; # (note the "+ 1"!)
  5461. $verbose and printf $out "$$self{INDENT}FLIR chunk %d of %d\n",
  5462. $chunkNum + 1, $chunksTot;
  5463. if (defined $flirTotal) {
  5464. # abort parsing FLIR if the total chunk count is inconsistent
  5465. undef $flirCount if $chunksTot != $flirTotal;
  5466. } else {
  5467. $flirCount = 0;
  5468. $flirTotal = $chunksTot;
  5469. }
  5470. if (defined $flirCount) {
  5471. if (defined $flirChunk[$chunkNum]) {
  5472. $self->WarnOnce('Duplicate FLIR chunk number(s)');
  5473. $flirChunk[$chunkNum] .= substr($$segDataPt, 8);
  5474. } else {
  5475. $flirChunk[$chunkNum] = substr($$segDataPt, 8);
  5476. }
  5477. # process the FLIR information if we have all of the chunks
  5478. if (++$flirCount >= $flirTotal) {
  5479. my $flir = '';
  5480. defined $_ and $flir .= $_ foreach @flirChunk;
  5481. undef @flirChunk; # free memory
  5482. my $tagTablePtr = GetTagTable('Image::ExifTool::FLIR::FFF');
  5483. my %dirInfo = (
  5484. DataPt => \$flir,
  5485. Parent => $markerName,
  5486. DirName => 'FLIR',
  5487. );
  5488. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5489. undef $flirCount; # prevent reprocessing
  5490. }
  5491. } else {
  5492. $self->WarnOnce('Invalid or extraneous FLIR chunk(s)');
  5493. }
  5494. } else {
  5495. # Hmmm. Could be XMP, let's see
  5496. my $processed;
  5497. if ($$segDataPt =~ /^http/ or $$segDataPt =~ /^XMP\0/ or $$segDataPt =~ /<exif:/) {
  5498. $dumpType = 'XMP';
  5499. # also try to parse XMP with a non-standard header
  5500. # (note: this non-standard XMP is ignored when writing)
  5501. my $start = ($$segDataPt =~ /^$xmpAPP1hdr/) ? length($xmpAPP1hdr) : 0;
  5502. my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
  5503. DirStart(\%dirInfo, $start);
  5504. $dirInfo{DirName} = $start ? 'XMP' : 'XML',
  5505. $processed = $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5506. if ($processed and not $start) {
  5507. $self->Warn('Non-standard header for APP1 XMP segment');
  5508. }
  5509. }
  5510. if ($verbose and not $processed) {
  5511. $self->Warn("Ignored APP1 segment length $length (unknown header)");
  5512. }
  5513. }
  5514. } elsif ($marker == 0xe2) { # APP2 (ICC Profile, FPXR, MPF, PreviewImage)
  5515. if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) {
  5516. $dumpType = 'ICC_Profile';
  5517. # must concatenate profile chunks (note: handle the case where
  5518. # some software erroneously writes zeros for the chunk counts)
  5519. my $chunkNum = Get8u($segDataPt, 12);
  5520. my $chunksTot = Get8u($segDataPt, 13);
  5521. $verbose and print $out "$$self{INDENT}ICC_Profile chunk $chunkNum of $chunksTot\n";
  5522. if (defined $iccChunksTotal) {
  5523. # abort parsing ICC_Profile if the total chunk count is inconsistent
  5524. undef $iccChunkCount if $chunksTot != $iccChunksTotal;
  5525. } else {
  5526. $iccChunkCount = 0;
  5527. $iccChunksTotal = $chunksTot;
  5528. $self->Warn('ICC_Profile chunk count is zero') if !$chunksTot;
  5529. }
  5530. if (defined $iccChunkCount) {
  5531. if (defined $iccChunk[$chunkNum]) {
  5532. $self->WarnOnce('Duplicate ICC_Profile chunk number(s)');
  5533. $iccChunk[$chunkNum] .= substr($$segDataPt, 14);
  5534. } else {
  5535. $iccChunk[$chunkNum] = substr($$segDataPt, 14);
  5536. }
  5537. # process profile if we have all of the chunks
  5538. if (++$iccChunkCount >= $iccChunksTotal) {
  5539. my $icc_profile = '';
  5540. defined $_ and $icc_profile .= $_ foreach @iccChunk;
  5541. undef @iccChunk; # free memory
  5542. my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
  5543. my %dirInfo = (
  5544. DataPt => \$icc_profile,
  5545. DataPos => $segPos + 14,
  5546. DataLen => length($icc_profile),
  5547. DirStart => 0,
  5548. DirLen => length($icc_profile),
  5549. Parent => $markerName,
  5550. );
  5551. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5552. undef $iccChunkCount; # prevent reprocessing
  5553. }
  5554. } else {
  5555. $self->WarnOnce('Invalid or extraneous ICC_Profile chunk(s)');
  5556. }
  5557. } elsif ($$segDataPt =~ /^FPXR\0/) {
  5558. next if $fast and $fast > 1; # skip processing for very fast
  5559. $dumpType = 'FPXR';
  5560. my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main');
  5561. # set flag if this is the last FPXR segment
  5562. $dirInfo{LastFPXR} = not ($nextMarker==$marker and $$nextSegDataPt=~/^FPXR\0/),
  5563. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5564. } elsif ($$segDataPt =~ /^MPF\0/) {
  5565. undef $dumpType; # (will be dumped here)
  5566. DirStart(\%dirInfo, 4, 4);
  5567. $dirInfo{Multi} = 1; # the MP Attribute IFD will be MPF1
  5568. if ($htmlDump) {
  5569. $self->HDump($segPos-4, 4, 'APP2 header', "Data size: $length bytes");
  5570. $self->HDump($segPos, 4, 'MPF header', 'APP2 data type: MPF');
  5571. $dumpEnd = $segPos + $length;
  5572. }
  5573. # extract the MPF information (it is in standard TIFF format)
  5574. my $tagTablePtr = GetTagTable('Image::ExifTool::MPF::Main');
  5575. $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  5576. } elsif ($$segDataPt =~ /^(|QVGA\0|BGTH)\xff\xd8\xff[\xdb\xe0\xe1]/) {
  5577. # Samsung/GE/GoPro="", BenQ DC C1220/Pentacon/Polaroid="QVGA\0",
  5578. # Digilife DDC-690/Rollei="BGTH"
  5579. $dumpType = 'Preview Image';
  5580. $preview = substr($$segDataPt, length($1));
  5581. } elsif ($preview) {
  5582. $dumpType = 'Preview Image';
  5583. $preview .= $$segDataPt;
  5584. }
  5585. if ($preview and $nextMarker ne $marker) {
  5586. $self->FoundTag('PreviewImage', $preview);
  5587. undef $preview;
  5588. }
  5589. } elsif ($marker == 0xe3) { # APP3 (Kodak "Meta", Stim)
  5590. if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
  5591. undef $dumpType; # (will be dumped here)
  5592. DirStart(\%dirInfo, 6, 6);
  5593. if ($htmlDump) {
  5594. $self->HDump($segPos-4, 10, 'APP3 Meta header');
  5595. $dumpEnd = $segPos + $length;
  5596. }
  5597. my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
  5598. $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  5599. } elsif ($$segDataPt =~ /^Stim\0/) {
  5600. undef $dumpType; # (will be dumped here)
  5601. DirStart(\%dirInfo, 6, 6);
  5602. if ($htmlDump) {
  5603. $self->HDump($segPos-4, 4, 'APP3 header', "Data size: $length bytes");
  5604. $self->HDump($segPos, 5, 'Stim header', 'APP3 data type: Stim');
  5605. $dumpEnd = $segPos + $length;
  5606. }
  5607. # extract the Stim information (it is in standard TIFF format)
  5608. my $tagTablePtr = GetTagTable('Image::ExifTool::Stim::Main');
  5609. $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  5610. } elsif ($$segDataPt =~ /^\xff\xd8\xff\xdb/) {
  5611. $dumpType = 'PreviewImage'; # (Samsung, HP, BenQ)
  5612. $preview = $$segDataPt;
  5613. }
  5614. if ($preview and $nextMarker ne 0xe4) { # this preview continues in APP4
  5615. $self->FoundTag('PreviewImage', $preview);
  5616. undef $preview;
  5617. }
  5618. } elsif ($marker == 0xe4) { # APP4 ("SCALADO", FPXR, PreviewImage)
  5619. if ($$segDataPt =~ /^SCALADO\0/ and $length >= 16) {
  5620. $dumpType = 'SCALADO';
  5621. my ($num, $idx, $len) = unpack('x8n2N', $$segDataPt);
  5622. # assume that the segments are in order and just concatinate them
  5623. $scalado = '' unless defined $scalado;
  5624. $scalado .= substr($$segDataPt, 16);
  5625. if ($idx == $num - 1) {
  5626. if ($len != length $scalado) {
  5627. $self->Warn('Possibly corrupted APP4 SCALADO data', 1);
  5628. }
  5629. my %dirInfo = (
  5630. Parent => $markerName,
  5631. DataPt => \$scalado,
  5632. );
  5633. my $tagTablePtr = GetTagTable('Image::ExifTool::Scalado::Main');
  5634. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5635. undef $scalado;
  5636. }
  5637. } elsif ($$segDataPt =~ /^FPXR\0/) {
  5638. next if $fast and $fast > 1; # skip processing for very fast
  5639. $dumpType = 'FPXR';
  5640. my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main');
  5641. # set flag if this is the last FPXR segment
  5642. $dirInfo{LastFPXR} = not ($nextMarker==$marker and $$nextSegDataPt=~/^FPXR\0/),
  5643. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5644. } elsif ($preview) {
  5645. # continued Samsung S1060 preview from APP3
  5646. $dumpType = 'PreviewImage';
  5647. $preview .= $$segDataPt;
  5648. }
  5649. # BenQ DC E1050 continues preview in APP5
  5650. if ($preview and $nextMarker ne 0xe5) {
  5651. $self->FoundTag('PreviewImage', $preview);
  5652. undef $preview;
  5653. }
  5654. } elsif ($marker == 0xe5) { # APP5 (Ricoh "RMETA")
  5655. if ($$segDataPt =~ /^RMETA\0/) {
  5656. # (NOTE: apparently these may span multiple segments, but I haven't seen
  5657. # a sample like this, so multi-segment support hasn't yet been implemented)
  5658. $dumpType = 'Ricoh RMETA';
  5659. DirStart(\%dirInfo, 6, 6);
  5660. my $tagTablePtr = GetTagTable('Image::ExifTool::Ricoh::RMETA');
  5661. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5662. } elsif ($preview) {
  5663. $dumpType = 'PreviewImage';
  5664. $preview .= $$segDataPt;
  5665. $self->FoundTag('PreviewImage', $preview);
  5666. undef $preview;
  5667. }
  5668. } elsif ($marker == 0xe6) { # APP6 (Toshiba EPPIM, NITF, HP_TDHD)
  5669. if ($$segDataPt =~ /^EPPIM\0/) {
  5670. undef $dumpType; # (will be dumped here)
  5671. DirStart(\%dirInfo, 6, 6);
  5672. if ($htmlDump) {
  5673. $self->HDump($segPos-4, 10, 'APP6 EPPIM header');
  5674. $dumpEnd = $segPos + $length;
  5675. }
  5676. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::EPPIM');
  5677. $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
  5678. } elsif ($$segDataPt =~ /^NITF\0/) {
  5679. $dumpType = 'NITF';
  5680. SetByteOrder('MM');
  5681. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::NITF');
  5682. DirStart(\%dirInfo, 5);
  5683. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5684. } elsif ($$segDataPt =~ /^TDHD\x01\0\0\0/ and $length > 12) {
  5685. # HP Photosmart R837 APP6 "TDHD" segment
  5686. $dumpType = 'TDHD';
  5687. my $tagTablePtr = GetTagTable('Image::ExifTool::HP::TDHD');
  5688. # (ignore first TDHD element because size includes 12-byte tag header)
  5689. DirStart(\%dirInfo, 12);
  5690. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5691. }
  5692. } elsif ($marker == 0xe7) { # APP7 (Pentax, Qualcomm)
  5693. if ($$segDataPt =~ /^PENTAX \0(II|MM)/) {
  5694. # found in K-3 images (is this multi-segment??)
  5695. SetByteOrder($1);
  5696. undef $dumpType; # (dump this ourself)
  5697. my $hdrLen = 10;
  5698. my $tagTablePtr = GetTagTable('Image::ExifTool::Pentax::Main');
  5699. DirStart(\%dirInfo, $hdrLen, 0);
  5700. $dirInfo{DirName} = 'Pentax APP7';
  5701. if ($htmlDump) {
  5702. $self->HDump($segPos-4, 4, 'APP7 header', "Data size: $length bytes");
  5703. $self->HDump($segPos, $hdrLen, 'Pentax header', 'APP7 data type: Pentax');
  5704. $dumpEnd = $segPos + $length;
  5705. }
  5706. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5707. } elsif ($$segDataPt =~ /^\x1aQualcomm Camera Attributes/) {
  5708. # found in HP iPAQ_VoiceMessenger
  5709. $dumpType = 'Qualcomm';
  5710. my $tagTablePtr = GetTagTable('Image::ExifTool::Qualcomm::Main');
  5711. DirStart(\%dirInfo, 27);
  5712. $dirInfo{DirName} = 'Qualcomm';
  5713. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5714. }
  5715. } elsif ($marker == 0xe8) { # APP8 (SPIFF)
  5716. # my sample SPIFF has 32 bytes of data, but spec states 30
  5717. if ($$segDataPt =~ /^SPIFF\0/ and $length == 32) {
  5718. $dumpType = 'SPIFF';
  5719. DirStart(\%dirInfo, 6);
  5720. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::SPIFF');
  5721. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5722. }
  5723. } elsif ($marker == 0xe9) { # APP9 (Media Jukebox)
  5724. if ($$segDataPt =~ /^Media Jukebox\0/ and $length > 22) {
  5725. $dumpType = 'MediaJukebox';
  5726. # (start parsing after the "<MJMD>")
  5727. DirStart(\%dirInfo, 22);
  5728. $dirInfo{DirName} = 'MediaJukebox';
  5729. require Image::ExifTool::XMP;
  5730. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::MediaJukebox');
  5731. $self->ProcessDirectory(\%dirInfo, $tagTablePtr, \&Image::ExifTool::XMP::ProcessXMP);
  5732. }
  5733. } elsif ($marker == 0xea) { # APP10 (PhotoStudio Unicode comments)
  5734. if ($$segDataPt =~ /^UNICODE\0/) {
  5735. $dumpType = 'PhotoStudio';
  5736. my $comment = $self->Decode(substr($$segDataPt,8), 'UCS2', 'MM');
  5737. $self->FoundTag('Comment', $comment);
  5738. }
  5739. } elsif ($marker == 0xeb) { # APP11 (JPEG-HDR)
  5740. if ($$segDataPt =~ /^HDR_RI /) {
  5741. $dumpType = 'JPEG-HDR';
  5742. my $dataPt = $segDataPt;
  5743. if (defined $combinedSegData) {
  5744. if ($$segDataPt =~ /~\0/g) {
  5745. $combinedSegData .= substr($$segDataPt,pos($$segDataPt));
  5746. } else {
  5747. $self->Warn('Invalid format for JPEG-HDR extended segment');
  5748. }
  5749. $dataPt = \$combinedSegData;
  5750. }
  5751. if ($nextMarker == $marker and $$nextSegDataPt =~ /^HDR_RI /) {
  5752. $combinedSegData = $$segDataPt unless defined $combinedSegData;
  5753. } else {
  5754. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::HDR');
  5755. my %dirInfo = ( DataPt => $dataPt );
  5756. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5757. undef $combinedSegData;
  5758. }
  5759. }
  5760. } elsif ($marker == 0xec) { # APP12 (Ducky, Picture Info)
  5761. if ($$segDataPt =~ /^Ducky/) {
  5762. $dumpType = 'Ducky';
  5763. DirStart(\%dirInfo, 5);
  5764. my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
  5765. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5766. } else {
  5767. my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::PictureInfo');
  5768. $self->ProcessDirectory(\%dirInfo, $tagTablePtr) and $dumpType = 'Picture Info';
  5769. }
  5770. } elsif ($marker == 0xed) { # APP13 (Photoshop, Adobe_CM)
  5771. my $isOld;
  5772. if ($$segDataPt =~ /^$psAPP13hdr/ or ($$segDataPt =~ /^$psAPP13old/ and $isOld=1)) {
  5773. $dumpType = 'Photoshop';
  5774. # add this data to the combined data if it exists
  5775. my $dataPt = $segDataPt;
  5776. if (defined $combinedSegData) {
  5777. $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
  5778. $dataPt = \$combinedSegData;
  5779. }
  5780. # peek ahead to see if the next segment is photoshop data too
  5781. if ($nextMarker == $marker and $$nextSegDataPt =~ /^$psAPP13hdr/) {
  5782. # initialize combined data if necessary
  5783. $combinedSegData = $$segDataPt unless defined $combinedSegData;
  5784. # (will handle the Photoshop data the next time around)
  5785. } else {
  5786. my $hdrLen = $isOld ? 27 : 14;
  5787. # process APP13 Photoshop record
  5788. my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
  5789. my %dirInfo = (
  5790. DataPt => $dataPt,
  5791. DataPos => $segPos,
  5792. DataLen => length $$dataPt,
  5793. DirStart => $hdrLen, # directory starts after identifier
  5794. DirLen => length($$dataPt) - $hdrLen,
  5795. Parent => $markerName,
  5796. );
  5797. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5798. undef $combinedSegData;
  5799. }
  5800. } elsif ($$segDataPt =~ /^Adobe_CM/) {
  5801. $dumpType = 'Adobe_CM';
  5802. SetByteOrder('MM');
  5803. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::AdobeCM');
  5804. DirStart(\%dirInfo, 8);
  5805. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5806. }
  5807. } elsif ($marker == 0xee) { # APP14 (Adobe)
  5808. if ($$segDataPt =~ /^Adobe/) {
  5809. # extract as a block if requested, or if copying tags from file
  5810. if ($$self{REQ_TAG_LOOKUP}{adobe} or
  5811. # (not extracted normally, so check TAGS_FROM_FILE)
  5812. ($$self{TAGS_FROM_FILE} and not $$self{EXCL_TAG_LOOKUP}{adobe}))
  5813. {
  5814. $self->FoundTag('Adobe', $$segDataPt);
  5815. }
  5816. $dumpType = 'Adobe';
  5817. SetByteOrder('MM');
  5818. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Adobe');
  5819. DirStart(\%dirInfo, 5);
  5820. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5821. }
  5822. } elsif ($marker == 0xef) { # APP15 (GraphicConverter)
  5823. if ($$segDataPt =~ /^Q\s*(\d+)/ and $length == 4) {
  5824. $dumpType = 'GraphicConverter';
  5825. my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::GraphConv');
  5826. $self->HandleTag($tagTablePtr, 'Q', $1);
  5827. }
  5828. } elsif ($marker == 0xfe) { # COM (JPEG comment)
  5829. $dumpType = 'Comment';
  5830. $$segDataPt =~ s/\0+$//; # some dumb softwares add null terminators
  5831. $self->FoundTag('Comment', $$segDataPt);
  5832. } elsif ($marker == 0x64) { # CME (J2C comment and extension)
  5833. $dumpType = 'Comment';
  5834. if ($length > 2) {
  5835. my $reg = unpack('n', $$segDataPt); # get registration value
  5836. my $val = substr($$segDataPt, 2);
  5837. $val = $self->Decode($val, 'Latin') if $reg == 1;
  5838. # (actually an extension for $reg==65535, but store as binary comment)
  5839. $self->FoundTag('Comment', ($reg==0 or $reg==65535) ? \$val : $val);
  5840. }
  5841. } elsif ($marker == 0x51) { # SIZ (J2C)
  5842. my ($w, $h) = unpack('x2N2', $$segDataPt);
  5843. $self->FoundTag('ImageWidth', $w);
  5844. $self->FoundTag('ImageHeight', $h);
  5845. } elsif (($marker & 0xf0) != 0xe0) {
  5846. $dumpType = "$markerName segment";
  5847. $desc = "[JPEG $markerName]"; # (other known JPEG segments)
  5848. }
  5849. if (defined $dumpType) {
  5850. if (not $dumpType and $$self{OPTIONS}{Unknown}) {
  5851. $self->Warn("Unknown $markerName segment", 1);
  5852. }
  5853. if ($htmlDump) {
  5854. $desc or $desc = $markerName . ($dumpType ? " $dumpType" : '') . ' segment';
  5855. $self->HDump($segPos-4, $length+4, $desc, $tip, 0x08);
  5856. $dumpEnd = $segPos + $length;
  5857. }
  5858. }
  5859. undef $$segDataPt;
  5860. }
  5861. # process extended XMP now if it existed
  5862. if (%extendedXMP) {
  5863. my $guid;
  5864. # GUID indicated by the last main XMP segment
  5865. my $goodGuid = $$self{VALUE}{HasExtendedXMP} || '';
  5866. # GUID of the extended XMP that we will process ('2' for all)
  5867. my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0;
  5868. $readGuid = $goodGuid if $readGuid eq '1';
  5869. foreach $guid (sort keys %extendedXMP) {
  5870. next unless length $guid == 32; # ignore other (internal) keys
  5871. my $extXMP = $extendedXMP{$guid};
  5872. my ($off, @offsets, $warn);
  5873. # make sure we have all chunks, and create a list of sorted offsets
  5874. for ($off=0; $off<$$extXMP{Size}; ) {
  5875. last unless defined $$extXMP{$off};
  5876. push @offsets, $off;
  5877. $off += length $$extXMP{$off};
  5878. }
  5879. unless ($off == $$extXMP{Size}) {
  5880. $self->Warn("Incomplete extended XMP (GUID $guid)");
  5881. next;
  5882. }
  5883. if ($guid eq $readGuid or $readGuid eq '2') {
  5884. $warn = 'Reading non-' if $guid ne $goodGuid;
  5885. my $buff = '';
  5886. # assemble XMP all together
  5887. $buff .= $$extXMP{$_} foreach @offsets;
  5888. my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
  5889. my %dirInfo = (
  5890. DataPt => \$buff,
  5891. Parent => 'APP1',
  5892. );
  5893. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  5894. } else {
  5895. $warn = 'Ignored ';
  5896. $warn .= 'non-' if $guid ne $goodGuid;
  5897. }
  5898. $self->Warn("${warn}standard extended XMP (GUID $guid)") if $warn;
  5899. delete $extendedXMP{$guid};
  5900. }
  5901. }
  5902. # calculate JPEGDigest if requested
  5903. if (@dqt) {
  5904. require Image::ExifTool::JPEGDigest;
  5905. Image::ExifTool::JPEGDigest::Calculate($self, \@dqt, $subSampling);
  5906. }
  5907. # issue necessary warnings
  5908. $self->Warn('Incomplete ICC_Profile record', 1) if defined $iccChunkCount;
  5909. $self->Warn('Incomplete FLIR record', 1) if defined $flirCount;
  5910. $self->Warn('Error reading PreviewImage', 1) if $$self{PreviewError};
  5911. $success or $self->Warn('JPEG format error');
  5912. pop @$path if @$path > $pn;
  5913. return 1;
  5914. }
  5915. #------------------------------------------------------------------------------
  5916. # Extract metadata from an Exiv2 EXV file
  5917. # Inputs: 0) ExifTool object reference, 1) dirInfo ref with RAF set
  5918. # Returns: 1 on success, 0 if this wasn't a valid JPEG file
  5919. sub ProcessEXV($$)
  5920. {
  5921. my ($self, $dirInfo) = @_;
  5922. return $self->ProcessJPEG($dirInfo);
  5923. }
  5924. #------------------------------------------------------------------------------
  5925. # Process EXIF file
  5926. # Inputs/Returns: same as ProcessTIFF
  5927. sub ProcessEXIF($$;$)
  5928. {
  5929. my ($self, $dirInfo, $tagTablePtr) = @_;
  5930. return $self->ProcessTIFF($dirInfo, $tagTablePtr);
  5931. }
  5932. #------------------------------------------------------------------------------
  5933. # Process TIFF data (wrapper for DoProcessTIFF to allow re-entry)
  5934. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
  5935. # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error
  5936. sub ProcessTIFF($$;$)
  5937. {
  5938. my ($self, $dirInfo, $tagTablePtr) = @_;
  5939. my $exifData = $$self{EXIF_DATA};
  5940. my $exifPos = $$self{EXIF_POS};
  5941. my $rtnVal = $self->DoProcessTIFF($dirInfo, $tagTablePtr);
  5942. # restore original EXIF information (in case ProcessTIFF is nested)
  5943. if (defined $exifData) {
  5944. $$self{EXIF_DATA} = $exifData;
  5945. $$self{EXIF_POS} = $exifPos;
  5946. }
  5947. return $rtnVal;
  5948. }
  5949. #------------------------------------------------------------------------------
  5950. # Process TIFF data
  5951. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
  5952. # Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error
  5953. sub DoProcessTIFF($$;$)
  5954. {
  5955. my ($self, $dirInfo, $tagTablePtr) = @_;
  5956. my $dataPt = $$dirInfo{DataPt};
  5957. my $fileType = $$dirInfo{Parent} || '';
  5958. my $raf = $$dirInfo{RAF};
  5959. my $base = $$dirInfo{Base} || 0;
  5960. my $outfile = $$dirInfo{OutFile};
  5961. my ($err, $sig, $canonSig, $otherSig);
  5962. # attempt to read TIFF header
  5963. $$self{EXIF_DATA} = '';
  5964. if ($raf) {
  5965. if ($outfile) {
  5966. $raf->Seek(0, 0) or return 0;
  5967. if ($base) {
  5968. $raf->Read($$dataPt, $base) == $base or return 0;
  5969. Write($outfile, $$dataPt) or $err = 1;
  5970. }
  5971. } else {
  5972. $raf->Seek($base, 0) or return 0;
  5973. }
  5974. # extract full EXIF block (for block copy) from EXIF file
  5975. my $amount = $fileType eq 'EXIF' ? 65536 * 8 : 8;
  5976. my $n = $raf->Read($$self{EXIF_DATA}, $amount);
  5977. if ($n < 8) {
  5978. return 0 if $n or not $outfile or $fileType ne 'EXIF';
  5979. # create EXIF file from scratch
  5980. delete $$self{EXIF_DATA};
  5981. undef $raf;
  5982. }
  5983. if ($n > 8) {
  5984. $raf->Seek(8, 0);
  5985. if ($n == $amount) {
  5986. $$self{EXIF_DATA} = substr($$self{EXIF_DATA}, 0, 8);
  5987. $self->Warn('EXIF too large to extract as a block'); #(shouldn't happen)
  5988. }
  5989. }
  5990. } elsif ($dataPt and length $$dataPt) {
  5991. # save a copy of the EXIF data
  5992. my $dirStart = $$dirInfo{DirStart} || 0;
  5993. my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart);
  5994. $$self{EXIF_DATA} = substr($$dataPt, $dirStart, $dirLen);
  5995. $self->VerboseDir('TIFF') if $$self{OPTIONS}{Verbose} and length($$self{INDENT}) > 2;
  5996. } elsif ($outfile) {
  5997. delete $$self{EXIF_DATA}; # create from scratch
  5998. } else {
  5999. $$self{EXIF_DATA} = '';
  6000. }
  6001. unless (defined $$self{EXIF_DATA}) {
  6002. # create TIFF information from scratch
  6003. if ($self->SetPreferredByteOrder() eq 'MM') {
  6004. $$self{EXIF_DATA} = "MM\0\x2a\0\0\0\x08";
  6005. } else {
  6006. $$self{EXIF_DATA} = "II\x2a\0\x08\0\0\0";
  6007. }
  6008. }
  6009. $$self{FIRST_EXIF_POS} = $base + $$self{BASE} unless defined $$self{FIRST_EXIF_POS};
  6010. $$self{EXIF_POS} = $base + $$self{BASE};
  6011. $dataPt = \$$self{EXIF_DATA};
  6012. # set byte ordering
  6013. my $byteOrder = substr($$dataPt,0,2);
  6014. SetByteOrder($byteOrder) or return 0;
  6015. # verify the byte ordering
  6016. my $identifier = Get16u($dataPt, 2);
  6017. # identifier is 0x2a for TIFF (but 0x4f52, 0x5352 or ?? for ORF)
  6018. # no longer do this because various files use different values
  6019. # (TIFF=0x2a, RW2/RWL=0x55, HDP=0xbc, BTF=0x2b, ORF=0x4f52/0x5352/0x????)
  6020. # return 0 unless $identifier == 0x2a;
  6021. # get offset to IFD0
  6022. my $offset = Get32u($dataPt, 4);
  6023. $offset >= 8 or return 0;
  6024. if ($raf) {
  6025. # check for canon or EXIF signature
  6026. # (Canon CR2 images should have an offset of 16, but it may be
  6027. # greater if edited by PhotoMechanic)
  6028. if ($identifier == 0x2a and $offset >= 16) {
  6029. $raf->Read($sig, 8) == 8 or return 0;
  6030. $$dataPt .= $sig;
  6031. if ($sig =~ /^(CR\x02\0|\xba\xb0\xac\xbb|ExifMeta)/) {
  6032. if ($sig eq 'ExifMeta') {
  6033. $self->SetFileType($fileType = 'EXIF');
  6034. $otherSig = $sig;
  6035. } else {
  6036. $fileType = $sig =~ /^CR/ ? 'CR2' : 'Canon 1D RAW';
  6037. $canonSig = $sig;
  6038. }
  6039. $self->HDump($base+8, 8, "[$fileType header]") if $$self{HTML_DUMP};
  6040. }
  6041. } elsif ($identifier == 0x55 and $fileType =~ /^(RAW|RW2|RWL|TIFF)$/) {
  6042. # panasonic RAW, RW2 or RWL file
  6043. my $magic;
  6044. # test for RW2/RWL magic number
  6045. if ($offset >= 0x18 and $raf->Read($magic, 16) and
  6046. $magic eq "\x88\xe7\x74\xd8\xf8\x25\x1d\x4d\x94\x7a\x6e\x77\x82\x2b\x5d\x6a")
  6047. {
  6048. $fileType = 'RW2' unless $fileType eq 'RWL';
  6049. $self->HDump($base + 8, 16, '[RW2/RWL header]') if $$self{HTML_DUMP};
  6050. $otherSig = $magic; # save signature for writing
  6051. } else {
  6052. $fileType = 'RAW';
  6053. }
  6054. $tagTablePtr = GetTagTable('Image::ExifTool::PanasonicRaw::Main');
  6055. } elsif ($fileType eq 'TIFF') {
  6056. if ($identifier == 0x2b) {
  6057. # this looks like a BigTIFF image
  6058. $raf->Seek(0);
  6059. require Image::ExifTool::BigTIFF;
  6060. return 1 if Image::ExifTool::BigTIFF::ProcessBTF($self, $dirInfo);
  6061. } elsif ($identifier == 0x4f52 or $identifier == 0x5352) {
  6062. # Olympus ORF image (set FileType now because base type is 'ORF')
  6063. $self->SetFileType($fileType = 'ORF');
  6064. } elsif ($identifier == 0x4352) {
  6065. $fileType = 'DCP';
  6066. } elsif ($byteOrder eq 'II' and ($identifier & 0xff) == 0xbc) {
  6067. $fileType = 'HDP'; # Windows HD Photo file
  6068. # check version number
  6069. my $ver = Get8u($dataPt, 3);
  6070. if ($ver > 1) {
  6071. $self->Error("Windows HD Photo version $ver files not yet supported");
  6072. return 1;
  6073. }
  6074. }
  6075. }
  6076. # we have a valid TIFF (or whatever) file
  6077. if ($fileType and not $$self{VALUE}{FileType}) {
  6078. my $lookup = $fileTypeLookup{$fileType};
  6079. $lookup = $fileTypeLookup{$lookup} unless ref $lookup or not $lookup;
  6080. # use file extension to pre-determine type if extension is TIFF-based or type is RAW
  6081. my $baseType = $lookup ? (ref $$lookup[0] ? $$lookup[0][0] : $$lookup[0]) : '';
  6082. my $t = ($baseType eq 'TIFF' or $fileType =~ /RAW/) ? $fileType : undef;
  6083. $self->SetFileType($t);
  6084. }
  6085. # don't process file if FastScan == 3
  6086. return 1 if not $outfile and $$self{OPTIONS}{FastScan} and $$self{OPTIONS}{FastScan} == 3;
  6087. }
  6088. my $ifdName = 'IFD0';
  6089. if (not $tagTablePtr or $$tagTablePtr{GROUPS}{0} eq 'EXIF') {
  6090. $self->FoundTag('ExifByteOrder', $byteOrder);
  6091. } else {
  6092. $ifdName = $$tagTablePtr{GROUPS}{1};
  6093. }
  6094. if ($$self{HTML_DUMP}) {
  6095. my $tip = sprintf("Byte order: %s endian\nIdentifier: 0x%.4x\n$ifdName offset: 0x%.4x",
  6096. ($byteOrder eq 'II') ? 'Little' : 'Big', $identifier, $offset);
  6097. $self->HDump($base, 8, 'TIFF header', $tip, 0);
  6098. }
  6099. # remember where we found the TIFF data (APP1, APP3, TIFF, NEF, etc...)
  6100. $$self{TIFF_TYPE} = $fileType;
  6101. # get reference to the main EXIF table
  6102. $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
  6103. # build directory information hash
  6104. my %dirInfo = (
  6105. Base => $base,
  6106. DataPt => $dataPt,
  6107. DataLen => length $$dataPt,
  6108. DataPos => 0,
  6109. DirStart => $offset,
  6110. DirLen => length($$dataPt) - $offset,
  6111. RAF => $raf,
  6112. DirName => $ifdName,
  6113. Parent => $fileType,
  6114. ImageData=> 'Main', # set flag to get information to copy main image data later
  6115. Multi => $$dirInfo{Multi},
  6116. );
  6117. # extract information from the image
  6118. unless ($outfile) {
  6119. # process the directory
  6120. $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
  6121. # process GeoTiff information if available
  6122. if ($$self{VALUE}{GeoTiffDirectory}) {
  6123. require Image::ExifTool::GeoTiff;
  6124. Image::ExifTool::GeoTiff::ProcessGeoTiff($self);
  6125. }
  6126. # process information in recognized trailers
  6127. if ($raf) {
  6128. my $trailInfo = IdentifyTrailer($raf);
  6129. if ($trailInfo) {
  6130. $$trailInfo{ScanForAFCP} = 1; # scan to find AFCP if necessary
  6131. $self->ProcessTrailers($trailInfo);
  6132. }
  6133. # dump any other known trailer (eg. A100 RAW Data)
  6134. if ($$self{HTML_DUMP} and $$self{KnownTrailer}) {
  6135. my $known = $$self{KnownTrailer};
  6136. $raf->Seek(0, 2);
  6137. my $len = $raf->Tell() - $$known{Start};
  6138. $len -= $$trailInfo{Offset} if $trailInfo; # account for other trailers
  6139. $self->HDump($$known{Start}, $len, "[$$known{Name}]") if $len > 0;
  6140. }
  6141. }
  6142. # update FileType if necessary now that we know more about the file
  6143. if ($$self{DNGVersion} and $$self{VALUE}{FileType} ne 'DNG') {
  6144. # override whatever FileType we set since we now know it is DNG
  6145. $self->OverrideFileType('DNG');
  6146. }
  6147. return 1;
  6148. }
  6149. #
  6150. # rewrite the image
  6151. #
  6152. if ($$dirInfo{NoTiffEnd}) {
  6153. delete $$self{TIFF_END};
  6154. } else {
  6155. # initialize TIFF_END so it will be updated by WriteExif()
  6156. $$self{TIFF_END} = 0;
  6157. }
  6158. if ($canonSig) {
  6159. # write Canon CR2 specially because it has a header we want to preserve,
  6160. # and possibly trailers added by the Canon utilities and/or PhotoMechanic
  6161. $dirInfo{OutFile} = $outfile;
  6162. require Image::ExifTool::CanonRaw;
  6163. Image::ExifTool::CanonRaw::WriteCR2($self, \%dirInfo, $tagTablePtr) or $err = 1;
  6164. } else {
  6165. # write TIFF header (8 bytes [plus optional signature] followed by IFD)
  6166. if ($fileType eq 'EXIF') {
  6167. $otherSig = 'ExifMeta'; # force this signature for all EXIF files
  6168. } elsif (not defined $otherSig) {
  6169. $otherSig = '';
  6170. }
  6171. my $offset = 8 + length($otherSig);
  6172. # construct tiff header
  6173. my $header = substr($$dataPt, 0, 4) . Set32u($offset) . $otherSig;
  6174. $dirInfo{NewDataPos} = $offset;
  6175. $dirInfo{HeaderPtr} = \$header;
  6176. # preserve padding between image data blocks in ORF images
  6177. # (otherwise dcraw has problems because it assumes fixed block spacing)
  6178. $dirInfo{PreserveImagePadding} = 1 if $fileType eq 'ORF' or $identifier != 0x2a;
  6179. my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
  6180. if (not defined $newData) {
  6181. $err = 1;
  6182. } elsif (length($newData)) {
  6183. # update header length in case more was added
  6184. my $hdrLen = length $header;
  6185. if ($hdrLen != 8) {
  6186. Set32u($hdrLen, \$header, 4);
  6187. # also update preview fixup if necessary
  6188. my $pi = $$self{PREVIEW_INFO};
  6189. $$pi{Fixup}{Start} += $hdrLen - 8 if $pi and $$pi{Fixup};
  6190. }
  6191. if ($$self{TIFF_TYPE} eq 'ARW' and not $err) {
  6192. # write any required ARW trailer and patch other ARW quirks
  6193. require Image::ExifTool::Sony;
  6194. my $errStr = Image::ExifTool::Sony::FinishARW($self, $dirInfo, \$newData,
  6195. $dirInfo{ImageData});
  6196. $errStr and $self->Error($errStr);
  6197. delete $dirInfo{ImageData}; # (was copied by FinishARW)
  6198. } else {
  6199. Write($outfile, $header, $newData) or $err = 1;
  6200. }
  6201. undef $newData; # free memory
  6202. }
  6203. # copy over image data now if necessary
  6204. if (ref $dirInfo{ImageData} and not $err) {
  6205. $self->CopyImageData($dirInfo{ImageData}, $outfile) or $err = 1;
  6206. delete $dirInfo{ImageData};
  6207. }
  6208. }
  6209. # make local copy of TIFF_END now (it may be reset when processing trailers)
  6210. my $tiffEnd = $$self{TIFF_END};
  6211. delete $$self{TIFF_END};
  6212. # rewrite trailers if they exist
  6213. if ($raf and $tiffEnd and not $err) {
  6214. my ($buf, $trailInfo);
  6215. $raf->Seek(0, 2) or $err = 1;
  6216. my $extra = $raf->Tell() - $tiffEnd;
  6217. # check for trailer and process if possible
  6218. for (;;) {
  6219. last unless $extra > 12;
  6220. $raf->Seek($tiffEnd); # seek back to end of image
  6221. $trailInfo = IdentifyTrailer($raf);
  6222. last unless $trailInfo;
  6223. my $tbuf = '';
  6224. $$trailInfo{OutFile} = \$tbuf; # rewrite trailer(s)
  6225. $$trailInfo{ScanForAFCP} = 1; # scan for AFCP if necessary
  6226. # rewrite all trailers to buffer
  6227. unless ($self->ProcessTrailers($trailInfo)) {
  6228. undef $trailInfo;
  6229. $err = 1;
  6230. last;
  6231. }
  6232. # calculate unused bytes before trailer
  6233. $extra = $$trailInfo{DataPos} - $tiffEnd;
  6234. last; # yes, the 'for' loop was just a cheap 'goto'
  6235. }
  6236. # ignore a single zero byte if used for padding
  6237. if ($extra > 0 and $tiffEnd & 0x01) {
  6238. $raf->Seek($tiffEnd, 0) or $err = 1;
  6239. $raf->Read($buf, 1) or $err = 1;
  6240. defined $buf and $buf eq "\0" and --$extra, ++$tiffEnd;
  6241. }
  6242. if ($extra > 0) {
  6243. my $known = $$self{KnownTrailer};
  6244. if ($$self{DEL_GROUP}{Trailer} and not $known) {
  6245. $self->VPrint(0, " Deleting unknown trailer ($extra bytes)\n");
  6246. ++$$self{CHANGED};
  6247. } elsif ($known) {
  6248. $self->VPrint(0, " Copying $$known{Name} ($extra bytes)\n");
  6249. $raf->Seek($tiffEnd, 0) or $err = 1;
  6250. CopyBlock($raf, $outfile, $extra) or $err = 1;
  6251. } else {
  6252. $raf->Seek($tiffEnd, 0) or $err = 1;
  6253. # preserve unknown trailer only if it contains non-null data
  6254. # (Photoshop CS adds a trailer with 2 null bytes)
  6255. my $size = $extra;
  6256. for (;;) {
  6257. my $n = $size > 65536 ? 65536 : $size;
  6258. $raf->Read($buf, $n) == $n or $err = 1, last;
  6259. if ($buf =~ /[^\0]/) {
  6260. $self->VPrint(0, " Preserving unknown trailer ($extra bytes)\n");
  6261. # copy the trailer since it contains non-null data
  6262. Write($outfile, "\0"x($extra-$size)) or $err = 1, last if $size != $extra;
  6263. Write($outfile, $buf) or $err = 1, last;
  6264. CopyBlock($raf, $outfile, $size-$n) or $err = 1 if $size > $n;
  6265. last;
  6266. }
  6267. $size -= $n;
  6268. next if $size > 0;
  6269. $self->VPrint(0, " Deleting blank trailer ($extra bytes)\n");
  6270. last;
  6271. }
  6272. }
  6273. }
  6274. # write trailer buffer if necessary
  6275. $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1 if $trailInfo;
  6276. # add any new trailers we are creating
  6277. my $trailPt = $self->AddNewTrailers();
  6278. Write($outfile, $$trailPt) or $err = 1 if $trailPt;
  6279. }
  6280. # check DNG version
  6281. if ($$self{DNGVersion}) {
  6282. my $ver = $$self{DNGVersion};
  6283. # currently support up to DNG version 1.4
  6284. unless ($ver =~ /^(\d+) (\d+)/ and "$1.$2" <= 1.4) {
  6285. $ver =~ tr/ /./;
  6286. $self->Error("DNG Version $ver not yet tested", 1);
  6287. }
  6288. }
  6289. return $err ? -1 : 1;
  6290. }
  6291. #------------------------------------------------------------------------------
  6292. # Return list of tag table keys (ignoring special keys)
  6293. # Inputs: 0) reference to tag table
  6294. # Returns: List of table keys (unsorted)
  6295. sub TagTableKeys($)
  6296. {
  6297. local $_;
  6298. my $tagTablePtr = shift;
  6299. my @keyList;
  6300. foreach (keys %$tagTablePtr) {
  6301. push(@keyList, $_) unless $specialTags{$_};
  6302. }
  6303. return @keyList;
  6304. }
  6305. #------------------------------------------------------------------------------
  6306. # GetTagTable
  6307. # Inputs: 0) table name
  6308. # Returns: tag table reference, or undefined if not found
  6309. # Notes: Always use this function instead of requiring module and using table
  6310. # directly since this function also does the following the first time the table
  6311. # is loaded:
  6312. # - requires new module if necessary
  6313. # - generates default GROUPS hash and Group 0 name from module name
  6314. # - registers Composite tags if Composite table found
  6315. # - saves descriptions for tags in specified table
  6316. # - generates default TAG_PREFIX to be used for unknown tags
  6317. sub GetTagTable($)
  6318. {
  6319. my $tableName = shift or return undef;
  6320. my $table = $allTables{$tableName};
  6321. unless ($table) {
  6322. no strict 'refs';
  6323. unless (%$tableName) {
  6324. # try to load module for this table
  6325. if ($tableName =~ /(.*)::/) {
  6326. my $module = $1;
  6327. if (eval "require $module") {
  6328. # load additional XMP modules if required
  6329. if (not %$tableName and $module eq 'Image::ExifTool::XMP') {
  6330. require 'Image/ExifTool/XMP2.pl';
  6331. }
  6332. } else {
  6333. $@ and warn $@;
  6334. }
  6335. }
  6336. unless (%$tableName) {
  6337. warn "Can't find table $tableName\n";
  6338. return undef;
  6339. }
  6340. }
  6341. no strict 'refs';
  6342. $table = \%$tableName;
  6343. use strict 'refs';
  6344. $$table{TABLE_NAME} = $tableName; # set table name
  6345. ($$table{SHORT_NAME} = $tableName) =~ s/^Image::ExifTool:://;
  6346. # set default group 0 and 1 from module name unless already specified
  6347. my $defaultGroups = $$table{GROUPS};
  6348. $defaultGroups or $defaultGroups = $$table{GROUPS} = { };
  6349. unless ($$defaultGroups{0} and $$defaultGroups{1}) {
  6350. if ($tableName =~ /Image::.*?::([^:]*)/) {
  6351. $$defaultGroups{0} = $1 unless $$defaultGroups{0};
  6352. $$defaultGroups{1} = $1 unless $$defaultGroups{1};
  6353. } else {
  6354. $$defaultGroups{0} = $tableName unless $$defaultGroups{0};
  6355. $$defaultGroups{1} = $tableName unless $$defaultGroups{1};
  6356. }
  6357. }
  6358. $$defaultGroups{2} = 'Other' unless $$defaultGroups{2};
  6359. if ($$defaultGroups{0} eq 'XMP' or $$table{NAMESPACE}) {
  6360. # initialize some XMP table defaults
  6361. require Image::ExifTool::XMP;
  6362. Image::ExifTool::XMP::RegisterNamespace($table); # register all table namespaces
  6363. # set default write/check procs
  6364. $$table{WRITE_PROC} = \&Image::ExifTool::XMP::WriteXMP unless $$table{WRITE_PROC};
  6365. $$table{CHECK_PROC} = \&Image::ExifTool::XMP::CheckXMP unless $$table{CHECK_PROC};
  6366. $$table{LANG_INFO} = \&Image::ExifTool::XMP::GetLangInfo unless $$table{LANG_INFO};
  6367. }
  6368. # generate a tag prefix for unknown tags if necessary
  6369. unless (defined $$table{TAG_PREFIX}) {
  6370. my $tagPrefix;
  6371. if ($tableName =~ /Image::.*?::(.*)::Main/ || $tableName =~ /Image::.*?::(.*)/) {
  6372. ($tagPrefix = $1) =~ s/::/_/g;
  6373. } else {
  6374. $tagPrefix = $tableName;
  6375. }
  6376. $$table{TAG_PREFIX} = $tagPrefix;
  6377. }
  6378. # set up the new table
  6379. SetupTagTable($table);
  6380. # add any user-defined tags
  6381. if (%UserDefined and $UserDefined{$tableName}) {
  6382. my $tagID;
  6383. foreach $tagID (TagTableKeys($UserDefined{$tableName})) {
  6384. next if $specialTags{$tagID};
  6385. delete $$table{$tagID}; # replace any existing entry
  6386. AddTagToTable($table, $tagID, $UserDefined{$tableName}{$tagID}, 1);
  6387. }
  6388. }
  6389. # remember order we loaded the tables in
  6390. push @tableOrder, $tableName;
  6391. # insert newly loaded table into list
  6392. $allTables{$tableName} = $table;
  6393. }
  6394. return $table;
  6395. }
  6396. #------------------------------------------------------------------------------
  6397. # Process an image directory
  6398. # Inputs: 0) ExifTool object reference, 1) directory information reference
  6399. # 2) tag table reference, 3) optional reference to processing procedure
  6400. # Returns: Result from processing (1=success)
  6401. sub ProcessDirectory($$$;$)
  6402. {
  6403. my ($self, $dirInfo, $tagTablePtr, $proc) = @_;
  6404. return 0 unless $tagTablePtr and $dirInfo;
  6405. # use default proc from tag table or EXIF proc as fallback if no proc specified
  6406. $proc or $proc = $$tagTablePtr{PROCESS_PROC} || \&Image::ExifTool::Exif::ProcessExif;
  6407. # set directory name from default group0 name if not done already
  6408. my $dirName = $$dirInfo{DirName};
  6409. unless ($dirName) {
  6410. $dirName = $$tagTablePtr{GROUPS}{0};
  6411. $dirName = $$tagTablePtr{GROUPS}{1} if $dirName =~ /^APP\d+$/; # (use specific APP name)
  6412. $$dirInfo{DirName} = $dirName;
  6413. }
  6414. # guard against cyclical recursion into the same directory
  6415. if (defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and
  6416. # directories don't overlap if the length is zero
  6417. ($$dirInfo{DirLen} or not defined $$dirInfo{DirLen}))
  6418. {
  6419. my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0);
  6420. if ($$self{PROCESSED}{$addr}) {
  6421. $self->Warn("$dirName pointer references previous $$self{PROCESSED}{$addr} directory");
  6422. # patch for bug in Windows phone 7.5 O/S that writes incorrect InteropIFD pointer
  6423. return 0 unless $dirName eq 'GPS' and $$self{PROCESSED}{$addr} eq 'InteropIFD';
  6424. }
  6425. $$self{PROCESSED}{$addr} = $dirName;
  6426. }
  6427. my $oldOrder = GetByteOrder();
  6428. my $oldIndent = $$self{INDENT};
  6429. my $oldDir = $$self{DIR_NAME};
  6430. $$self{LIST_TAGS} = { }; # don't build lists across different directories
  6431. $$self{INDENT} .= '| ';
  6432. $$self{DIR_NAME} = $dirName;
  6433. push @{$$self{PATH}}, $dirName;
  6434. # process the directory
  6435. my $rtnVal = &$proc($self, $dirInfo, $tagTablePtr);
  6436. pop @{$$self{PATH}};
  6437. $$self{INDENT} = $oldIndent;
  6438. $$self{DIR_NAME} = $oldDir;
  6439. SetByteOrder($oldOrder);
  6440. return $rtnVal;
  6441. }
  6442. #------------------------------------------------------------------------------
  6443. # Get Metadata path
  6444. # Inputs: 0) Exiftool object ref
  6445. # Return: Metadata path string
  6446. sub MetadataPath($)
  6447. {
  6448. my $self = shift;
  6449. return join '-', @{$$self{PATH}}
  6450. }
  6451. #------------------------------------------------------------------------------
  6452. # Get standardized file extension
  6453. # Inputs: 0) file name
  6454. # Returns: standardized extension (all uppercase), or undefined if no extension
  6455. sub GetFileExtension($)
  6456. {
  6457. my $filename = shift;
  6458. my $fileExt;
  6459. if ($filename and $filename =~ /^.*\.([^.]+)$/s) {
  6460. $fileExt = uc($1); # change extension to upper case
  6461. # convert TIF extension to TIFF because we use the
  6462. # extension for the file type tag of TIFF images
  6463. $fileExt eq 'TIF' and $fileExt = 'TIFF';
  6464. }
  6465. return $fileExt;
  6466. }
  6467. #------------------------------------------------------------------------------
  6468. # Get list of tag information hashes for given tag ID
  6469. # Inputs: 0) Tag table reference, 1) tag ID
  6470. # Returns: Array of tag information references
  6471. # Notes: Generates tagInfo hash if necessary
  6472. sub GetTagInfoList($$)
  6473. {
  6474. my ($tagTablePtr, $tagID) = @_;
  6475. my $tagInfo = $$tagTablePtr{$tagID};
  6476. if ($specialTags{$tagID}) {
  6477. # (hopefully this won't happen)
  6478. warn "Tag $tagID conflicts with internal ExifTool variable in $$tagTablePtr{TABLE_NAME}\n";
  6479. } elsif (ref $tagInfo eq 'HASH') {
  6480. return ($tagInfo);
  6481. } elsif (ref $tagInfo eq 'ARRAY') {
  6482. return @$tagInfo;
  6483. } elsif ($tagInfo) {
  6484. # create hash with name
  6485. $tagInfo = $$tagTablePtr{$tagID} = { Name => $tagInfo };
  6486. return ($tagInfo);
  6487. }
  6488. return ();
  6489. }
  6490. #------------------------------------------------------------------------------
  6491. # Find tag information, processing conditional tags
  6492. # Inputs: 0) ExifTool object reference, 1) tagTable pointer, 2) tag ID
  6493. # 3) optional value reference, 4) optional format type, 5) optional value count
  6494. # Returns: pointer to tagInfo hash, undefined if none found, or '' if $valPt needed
  6495. # Notes: You should always call this routine to find a tag in a table because
  6496. # this routine will evaluate conditional tags.
  6497. # Arguments 3-5 are only required if the information type allows $valPt, $format and/or
  6498. # $count in a Condition, and if not given when needed this routine returns ''.
  6499. sub GetTagInfo($$$;$$$)
  6500. {
  6501. my ($self, $tagTablePtr, $tagID) = @_;
  6502. my ($valPt, $format, $count);
  6503. my @infoArray = GetTagInfoList($tagTablePtr, $tagID);
  6504. # evaluate condition
  6505. my $tagInfo;
  6506. foreach $tagInfo (@infoArray) {
  6507. my $condition = $$tagInfo{Condition};
  6508. if ($condition) {
  6509. ($valPt, $format, $count) = splice(@_, 3) if @_ > 3;
  6510. return '' if $condition =~ /\$(valPt|format|count)\b/ and not defined $valPt;
  6511. # set old value for use in condition if needed
  6512. local $SIG{'__WARN__'} = \&SetWarning;
  6513. undef $evalWarning;
  6514. #### eval Condition ($self, [$valPt, $format, $count])
  6515. unless (eval $condition) {
  6516. $@ and $evalWarning = $@;
  6517. $self->Warn("Condition $$tagInfo{Name}: " . CleanWarning()) if $evalWarning;
  6518. next;
  6519. }
  6520. }
  6521. if ($$tagInfo{Unknown} and not $$self{OPTIONS}{Unknown} and not $$self{OPTIONS}{Verbose}) {
  6522. # don't return Unknown tags unless that option is set
  6523. return undef;
  6524. }
  6525. # return the tag information we found
  6526. return $tagInfo;
  6527. }
  6528. # generate information for unknown tags (numerical only) if required
  6529. if (not $tagInfo and ($$self{OPTIONS}{Unknown} or $$self{OPTIONS}{Verbose}) and
  6530. $tagID =~ /^\d+$/ and not $$self{NO_UNKNOWN})
  6531. {
  6532. my $printConv;
  6533. if (defined $$tagTablePtr{PRINT_CONV}) {
  6534. $printConv = $$tagTablePtr{PRINT_CONV};
  6535. } else {
  6536. # limit length of printout (can be very long)
  6537. $printConv = 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val';
  6538. }
  6539. my $hex = sprintf("0x%.4x", $tagID);
  6540. my $prefix = $$tagTablePtr{TAG_PREFIX};
  6541. $tagInfo = {
  6542. Name => "${prefix}_$hex",
  6543. Description => MakeDescription($prefix, $hex),
  6544. Unknown => 1,
  6545. Writable => 0, # can't write unknown tags
  6546. PrintConv => $printConv,
  6547. };
  6548. # add tag information to table
  6549. AddTagToTable($tagTablePtr, $tagID, $tagInfo);
  6550. } else {
  6551. undef $tagInfo;
  6552. }
  6553. return $tagInfo;
  6554. }
  6555. #------------------------------------------------------------------------------
  6556. # Add new tag to table (must use this routine to add new tags to a table)
  6557. # Inputs: 0) reference to tag table, 1) tag ID
  6558. # 2) [optional] reference to tag information hash or simply tag name
  6559. # 3) [optional] flag to avoid adding prefix when generating tag name
  6560. # Notes: - will not overwrite existing entry in table
  6561. # - info need contain no entries when this routine is called
  6562. sub AddTagToTable($$;$$)
  6563. {
  6564. my ($tagTablePtr, $tagID, $tagInfo, $noPrefix) = @_;
  6565. # generate tag info hash if necessary
  6566. $tagInfo = $tagInfo ? { Name => $tagInfo } : { } unless ref $tagInfo eq 'HASH';
  6567. # define necessary entries in information hash
  6568. if ($$tagInfo{Groups}) {
  6569. # fill in default groups from table GROUPS
  6570. foreach (keys %{$$tagTablePtr{GROUPS}}) {
  6571. next if $$tagInfo{Groups}{$_};
  6572. $$tagInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_};
  6573. }
  6574. } else {
  6575. $$tagInfo{Groups} = { %{$$tagTablePtr{GROUPS}} };
  6576. }
  6577. $$tagInfo{Flags} and ExpandFlags($tagInfo);
  6578. $$tagInfo{GotGroups} = 1,
  6579. $$tagInfo{Table} = $tagTablePtr;
  6580. $$tagInfo{TagID} = $tagID;
  6581. my $name = $$tagInfo{Name};
  6582. if (defined $name) {
  6583. $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
  6584. } else {
  6585. # construct a name from the tag ID
  6586. $name = $tagID;
  6587. $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
  6588. $name = ucfirst $name; # start with uppercase
  6589. # add prefix if specified
  6590. my $prefix = $$tagTablePtr{TAG_PREFIX};
  6591. if ($prefix and not $noPrefix) {
  6592. # make description to prevent tagID from getting mangled by MakeDescription()
  6593. $$tagInfo{Description} = MakeDescription($prefix, $name);
  6594. $name = "${prefix}_$name";
  6595. }
  6596. }
  6597. # tag names must be at least 2 characters long and prefer them to start with a letter
  6598. $name = "Tag$name" if length($name) < 2 or $name !~ /^[A-Z]/i;
  6599. $$tagInfo{Name} = $name;
  6600. # add tag to table, but never overwrite existing entries (could potentially happen
  6601. # if someone thinks there isn't any tagInfo because a condition wasn't satisfied)
  6602. unless (defined $$tagTablePtr{$tagID} or $specialTags{$tagID}) {
  6603. $$tagTablePtr{$tagID} = $tagInfo;
  6604. }
  6605. }
  6606. #------------------------------------------------------------------------------
  6607. # Handle simple extraction of new tag information
  6608. # Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
  6609. # 4-N) parameters hash: Index, DataPt, DataPos, Base, Start, Size, Parent,
  6610. # TagInfo, ProcessProc, RAF
  6611. # Returns: tag key or undef if tag not found
  6612. # Notes: if value is not defined, it is extracted from DataPt using TagInfo
  6613. # Format and Count if provided
  6614. sub HandleTag($$$$;%)
  6615. {
  6616. my ($self, $tagTablePtr, $tag, $val, %parms) = @_;
  6617. my $verbose = $$self{OPTIONS}{Verbose};
  6618. my $pfmt = $parms{Format};
  6619. my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag, \$val, $pfmt, $parms{Count});
  6620. my $dataPt = $parms{DataPt};
  6621. my ($subdir, $format, $count, $size, $noTagInfo, $rational);
  6622. if ($tagInfo) {
  6623. $subdir = $$tagInfo{SubDirectory}
  6624. } else {
  6625. return undef unless $verbose;
  6626. $tagInfo = { Name => "tag $tag" }; # create temporary tagInfo hash
  6627. $noTagInfo = 1;
  6628. }
  6629. # read value if not done already (not necessary for subdir)
  6630. unless (defined $val or ($subdir and not $$tagInfo{Writable})) {
  6631. my $start = $parms{Start} || 0;
  6632. my $dLen = $dataPt ? length($$dataPt) : -1;
  6633. my $size = $parms{Size};
  6634. $size = $dLen unless defined $size;
  6635. # read from data in memory if possible
  6636. if ($start >= 0 and $start + $size <= $dLen) {
  6637. $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT};
  6638. $format = $pfmt if not $format and $pfmt and $formatSize{$pfmt};
  6639. if ($format) {
  6640. $val = ReadValue($dataPt, $start, $format, $$tagInfo{Count}, $size, \$rational);
  6641. } else {
  6642. $val = substr($$dataPt, $start, $size);
  6643. }
  6644. } else {
  6645. $self->Warn("Error extracting value for $$tagInfo{Name}");
  6646. return undef;
  6647. }
  6648. }
  6649. # do verbose print if necessary
  6650. if ($verbose) {
  6651. undef $tagInfo if $noTagInfo;
  6652. $parms{Value} = $val;
  6653. $parms{Value} .= " ($rational)" if defined $rational;
  6654. $parms{Table} = $tagTablePtr;
  6655. if ($format) {
  6656. $count or $count = int(($parms{Size} || 0) / ($formatSize{$format} || 1));
  6657. $parms{Format} = $format . "[$count]";
  6658. }
  6659. $self->VerboseInfo($tag, $tagInfo, %parms);
  6660. }
  6661. if ($tagInfo) {
  6662. if ($subdir) {
  6663. my $subdirStart = $parms{Start};
  6664. my $subdirLen = $parms{Size};
  6665. if ($$subdir{Start}) {
  6666. my $valuePtr = 0;
  6667. #### eval Start ($valuePtr)
  6668. my $off = eval $$subdir{Start};
  6669. $subdirStart += $off;
  6670. $subdirLen -= $off;
  6671. }
  6672. $dataPt or $dataPt = \$val;
  6673. # process subdirectory information
  6674. my %dirInfo = (
  6675. DirName => $$subdir{DirName} || $$tagInfo{Name},
  6676. DataPt => $dataPt,
  6677. DataLen => length $$dataPt,
  6678. DataPos => $parms{DataPos},
  6679. DirStart => $subdirStart,
  6680. DirLen => $subdirLen,
  6681. Parent => $parms{Parent},
  6682. Base => $parms{Base},
  6683. Multi => $$subdir{Multi},
  6684. TagInfo => $tagInfo,
  6685. RAF => $parms{RAF},
  6686. );
  6687. my $oldOrder = GetByteOrder();
  6688. SetByteOrder($$subdir{ByteOrder}) if $$subdir{ByteOrder};
  6689. my $subTablePtr = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
  6690. $self->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc} || $parms{ProcessProc});
  6691. SetByteOrder($oldOrder);
  6692. # return now unless directory is writable as a block
  6693. return undef unless $$tagInfo{Writable};
  6694. }
  6695. my $key = $self->FoundTag($tagInfo, $val);
  6696. # save original components of rational numbers
  6697. $$self{RATIONAL}{$key} = $rational if defined $rational and defined $key;
  6698. return $key;
  6699. }
  6700. return undef;
  6701. }
  6702. #------------------------------------------------------------------------------
  6703. # Add tag to hash of extracted information
  6704. # Inputs: 0) ExifTool object reference
  6705. # 1) reference to tagInfo hash or tag name
  6706. # 2) data value (or reference to require hash if Composite)
  6707. # Returns: tag key or undef if no value
  6708. sub FoundTag($$$)
  6709. {
  6710. local $_;
  6711. my ($self, $tagInfo, $value) = @_;
  6712. my ($tag, $noListDel);
  6713. if (ref $tagInfo eq 'HASH') {
  6714. $tag = $$tagInfo{Name} or warn("No tag name\n"), return undef;
  6715. } else {
  6716. $tag = $tagInfo;
  6717. # look for tag in Extra
  6718. $tagInfo = $self->GetTagInfo(GetTagTable('Image::ExifTool::Extra'), $tag);
  6719. # make temporary hash if tag doesn't exist in Extra
  6720. # (not advised to do this since the tag won't show in list)
  6721. $tagInfo or $tagInfo = { Name => $tag, Groups => \%allGroupsExifTool };
  6722. $$self{OPTIONS}{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);
  6723. }
  6724. # get tag priority
  6725. my $priority = $$tagInfo{Priority};
  6726. unless (defined $priority) {
  6727. $priority = $$tagInfo{Table}{PRIORITY};
  6728. $priority = 0 if not defined $priority and $$tagInfo{Avoid};
  6729. }
  6730. my $valueHash = $$self{VALUE};
  6731. if ($$tagInfo{RawConv}) {
  6732. # initialize @val for use in Composite RawConv expressions
  6733. my @val;
  6734. if (ref $value eq 'HASH') {
  6735. foreach (keys %$value) { $val[$_] = $$valueHash{$$value{$_}}; }
  6736. }
  6737. my $conv = $$tagInfo{RawConv};
  6738. local $SIG{'__WARN__'} = \&SetWarning;
  6739. undef $evalWarning;
  6740. if (ref $conv eq 'CODE') {
  6741. $value = &$conv($value, $self);
  6742. } else {
  6743. my $val = $value; # do this so eval can use $val
  6744. # NOTE: RawConv is also evaluated in Writer.pl
  6745. #### eval RawConv ($self, $val, $tag, $tagInfo, $priority)
  6746. $value = eval $conv;
  6747. $@ and $evalWarning = $@;
  6748. }
  6749. $self->Warn("RawConv $tag: " . CleanWarning()) if $evalWarning;
  6750. return undef unless defined $value;
  6751. }
  6752. # handle duplicate tag names
  6753. if (defined $$valueHash{$tag}) {
  6754. # add to list if there is an active list for this tag
  6755. if ($$self{LIST_TAGS}{$tagInfo}) {
  6756. $tag = $$self{LIST_TAGS}{$tagInfo}; # use key from previous list tag
  6757. if (defined $$self{NO_LIST}) {
  6758. # accumulate list in TAG_EXTRA "NoList" element
  6759. if (defined $$self{TAG_EXTRA}{$tag}{NoList}) {
  6760. push @{$$self{TAG_EXTRA}{$tag}{NoList}}, $value;
  6761. } else {
  6762. $$self{TAG_EXTRA}{$tag}{NoList} = [ $$valueHash{$tag}, $value ];
  6763. }
  6764. $noListDel = 1; # set flag to delete this tag if re-listed
  6765. } else {
  6766. if (ref $$valueHash{$tag} ne 'ARRAY') {
  6767. $$valueHash{$tag} = [ $$valueHash{$tag} ];
  6768. }
  6769. push @{$$valueHash{$tag}}, $value;
  6770. return $tag; # return without creating a new entry
  6771. }
  6772. }
  6773. # get next available tag key
  6774. my $nextInd = $$self{DUPL_TAG}{$tag} = ($$self{DUPL_TAG}{$tag} || 0) + 1;
  6775. my $nextTag = "$tag ($nextInd)";
  6776. #
  6777. # take tag with highest priority
  6778. #
  6779. # promote existing 0-priority tag so it takes precedence over a new 0-tag
  6780. # (unless old tag was a sub-document and new tag isn't)
  6781. my $oldPriority = $$self{PRIORITY}{$tag};
  6782. unless ($oldPriority) {
  6783. if ($$self{DOC_NUM} or not $$self{TAG_EXTRA}{$tag} or
  6784. not $$self{TAG_EXTRA}{$tag}{G3})
  6785. {
  6786. $oldPriority = 1;
  6787. } else {
  6788. $oldPriority = 0; # don't promote sub-document tag over main document
  6789. }
  6790. }
  6791. # set priority for this tag
  6792. if (defined $priority) {
  6793. # increase 0-priority tags if this is the priority directory
  6794. $priority = 1 if not $priority and $$self{DIR_NAME} and
  6795. $$self{DIR_NAME} eq $$self{PRIORITY_DIR};
  6796. } elsif ($$self{DIR_NAME} and $$self{LOW_PRIORITY_DIR}{$$self{DIR_NAME}}) {
  6797. $priority = 0; # default is 0 for a LOW_PRIORITY_DIR
  6798. } else {
  6799. $priority = 1; # the normal default
  6800. }
  6801. if ($priority >= $oldPriority and not $$self{DOC_NUM} and not $noListDel) {
  6802. # move existing tag out of the way since this tag is higher priority
  6803. # (NOTE: any new members added here must also be added to DeleteTag())
  6804. $$self{MOVED_KEY} = $nextTag; # used in BuildCompositeTags()
  6805. $$self{PRIORITY}{$nextTag} = $$self{PRIORITY}{$tag};
  6806. $$valueHash{$nextTag} = $$valueHash{$tag};
  6807. $$self{FILE_ORDER}{$nextTag} = $$self{FILE_ORDER}{$tag};
  6808. my $oldInfo = $$self{TAG_INFO}{$nextTag} = $$self{TAG_INFO}{$tag};
  6809. foreach ('TAG_EXTRA','RATIONAL') {
  6810. if ($$self{$_}{$tag}) {
  6811. $$self{$_}{$nextTag} = $$self{$_}{$tag};
  6812. delete $$self{$_}{$tag};
  6813. }
  6814. }
  6815. delete $$self{BOTH}{$tag};
  6816. # update tag key for list if necessary
  6817. $$self{LIST_TAGS}{$oldInfo} = $nextTag if $$self{LIST_TAGS}{$oldInfo};
  6818. } else {
  6819. $tag = $nextTag; # don't override the existing tag
  6820. }
  6821. $$self{PRIORITY}{$tag} = $priority;
  6822. $$self{TAG_EXTRA}{$tag}{NoListDel} = 1 if $noListDel;
  6823. } elsif ($priority) {
  6824. # set tag priority (only if exists and is non-zero)
  6825. $$self{PRIORITY}{$tag} = $priority;
  6826. }
  6827. # save the raw value, file order, tagInfo ref, group1 name,
  6828. # and tag key for lists if necessary
  6829. $$valueHash{$tag} = $value;
  6830. $$self{FILE_ORDER}{$tag} = ++$$self{NUM_FOUND};
  6831. $$self{TAG_INFO}{$tag} = $tagInfo;
  6832. # set dynamic groups 0, 1 and 3 if necessary
  6833. $$self{TAG_EXTRA}{$tag}{G0} = $$self{SET_GROUP0} if $$self{SET_GROUP0};
  6834. $$self{TAG_EXTRA}{$tag}{G1} = $$self{SET_GROUP1} if $$self{SET_GROUP1};
  6835. if ($$self{DOC_NUM}) {
  6836. $$self{TAG_EXTRA}{$tag}{G3} = $$self{DOC_NUM};
  6837. if ($$self{DOC_NUM} =~ /^(\d+)/) {
  6838. # keep track of maximum 1st-level sub-document number
  6839. $$self{DOC_COUNT} = $1 unless $$self{DOC_COUNT} >= $1;
  6840. }
  6841. }
  6842. # save path if requested
  6843. $$self{TAG_EXTRA}{$tag}{G5} = $self->MetadataPath() if $$self{OPTIONS}{SavePath};
  6844. # remember this tagInfo if we will be accumulating values in a list
  6845. # (but don't override earlier list if this may be deleted by NoListDel flag)
  6846. if ($$tagInfo{List} and not $$self{NO_LIST} and not $noListDel) {
  6847. $$self{LIST_TAGS}{$tagInfo} = $tag;
  6848. }
  6849. return $tag;
  6850. }
  6851. #------------------------------------------------------------------------------
  6852. # Make current directory the priority directory if not set already
  6853. # Inputs: 0) ExifTool object reference
  6854. sub SetPriorityDir($)
  6855. {
  6856. my $self = shift;
  6857. $$self{PRIORITY_DIR} = $$self{DIR_NAME} unless $$self{PRIORITY_DIR};
  6858. }
  6859. #------------------------------------------------------------------------------
  6860. # Set family 0 or 1 group name specific to this tag instance
  6861. # Inputs: 0) ExifTool ref, 1) tag key, 2) group name, 3) family (default 1)
  6862. sub SetGroup($$$;$)
  6863. {
  6864. my ($self, $tagKey, $extra, $fam) = @_;
  6865. $$self{TAG_EXTRA}{$tagKey}{defined $fam ? "G$fam" : 'G1'} = $extra;
  6866. }
  6867. #------------------------------------------------------------------------------
  6868. # Delete specified tag
  6869. # Inputs: 0) ExifTool object ref, 1) tag key
  6870. sub DeleteTag($$)
  6871. {
  6872. my ($self, $tag) = @_;
  6873. delete $$self{VALUE}{$tag};
  6874. delete $$self{FILE_ORDER}{$tag};
  6875. delete $$self{TAG_INFO}{$tag};
  6876. delete $$self{TAG_EXTRA}{$tag};
  6877. delete $$self{PRIORITY}{$tag};
  6878. delete $$self{RATIONAL}{$tag};
  6879. delete $$self{BOTH}{$tag};
  6880. }
  6881. #------------------------------------------------------------------------------
  6882. # Escape all elements of a value
  6883. # Inputs: 0) value, 1) escape proc
  6884. sub DoEscape($$)
  6885. {
  6886. my ($val, $key);
  6887. if (not ref $_[0]) {
  6888. $_[0] = &{$_[1]}($_[0]);
  6889. } elsif (ref $_[0] eq 'ARRAY') {
  6890. foreach $val (@{$_[0]}) {
  6891. DoEscape($val, $_[1]);
  6892. }
  6893. } elsif (ref $_[0] eq 'HASH') {
  6894. foreach $key (keys %{$_[0]}) {
  6895. DoEscape($_[0]{$key}, $_[1]);
  6896. }
  6897. }
  6898. }
  6899. #------------------------------------------------------------------------------
  6900. # Set the FileType and MIMEType tags
  6901. # Inputs: 0) ExifTool object reference
  6902. # 1) Optional file type (uses FILE_TYPE if not specified)
  6903. # 2) Optional MIME type (uses our lookup if not specified)
  6904. # 3) Optional recommended extension (converted to lower case; uses FileType if undef)
  6905. # Notes: Will NOT set file type twice (subsequent calls ignored)
  6906. sub SetFileType($;$$$)
  6907. {
  6908. my ($self, $fileType, $mimeType, $normExt) = @_;
  6909. unless ($$self{VALUE}{FileType} and not $$self{DOC_NUM}) {
  6910. my $baseType = $$self{FILE_TYPE};
  6911. my $ext = $$self{FILE_EXT};
  6912. $fileType or $fileType = $baseType;
  6913. # handle sub-types which are identified by extension
  6914. if (defined $ext and $ext ne $fileType and not $$self{DOC_NUM}) {
  6915. my ($f,$e) = @fileTypeLookup{$fileType,$ext};
  6916. if (ref $f eq 'ARRAY' and ref $e eq 'ARRAY' and $$f[0] eq $$e[0]) {
  6917. # make sure $fileType was a root type and not another sub-type
  6918. $fileType = $ext if $$f[0] eq $fileType or not $fileTypeLookup{$$f[0]};
  6919. }
  6920. }
  6921. $mimeType or $mimeType = $mimeType{$fileType};
  6922. # use base file type if necessary (except if 'TIFF', which is a special case)
  6923. $mimeType = $mimeType{$baseType} unless $mimeType or $baseType eq 'TIFF';
  6924. unless (defined $normExt) {
  6925. $normExt = $fileTypeExt{$fileType};
  6926. $normExt = $fileType unless defined $normExt;
  6927. }
  6928. $self->FoundTag('FileType', $fileType);
  6929. $self->FoundTag('FileTypeExtension', uc $normExt);
  6930. $self->FoundTag('MIMEType', $mimeType || 'application/unknown');
  6931. }
  6932. }
  6933. #------------------------------------------------------------------------------
  6934. # Override the FileType and MIMEType tags
  6935. # Inputs: 0) ExifTool object ref, 1) file type
  6936. # Notes: does nothing if FileType was not previously defined (ie. when writing)
  6937. sub OverrideFileType($$)
  6938. {
  6939. my ($self, $fileType) = @_;
  6940. if (defined $$self{VALUE}{FileType} and $fileType ne $$self{VALUE}{FileType}) {
  6941. $$self{VALUE}{FileType} = $fileType;
  6942. my $normExt = $fileTypeExt{$fileType};
  6943. $normExt = $fileType unless defined $normExt;
  6944. $$self{VALUE}{FileTypeExtension} = uc $normExt;
  6945. # only override MIME type if unique for the derived file type
  6946. $$self{VALUE}{MIMEType} = $mimeType{$fileType} if $mimeType{$fileType};
  6947. if ($$self{OPTIONS}{Verbose}) {
  6948. $self->VPrint(0,"$$self{INDENT}FileType [override] = $fileType\n");
  6949. $self->VPrint(0,"$$self{INDENT}MIMEType [override] = $$self{VALUE}{MIMEType}\n");
  6950. }
  6951. }
  6952. }
  6953. #------------------------------------------------------------------------------
  6954. # Modify the value of the MIMEType tag
  6955. # Inputs: 0) ExifTool object reference, 1) file or MIME type
  6956. # Notes: combines existing type with new type: ie) a/b + c/d => c/b-d
  6957. sub ModifyMimeType($;$)
  6958. {
  6959. my ($self, $mime) = @_;
  6960. $mime =~ m{/} or $mime = $mimeType{$mime} or return;
  6961. my $old = $$self{VALUE}{MIMEType};
  6962. if (defined $old) {
  6963. my ($a, $b) = split '/', $old;
  6964. my ($c, $d) = split '/', $mime;
  6965. $d =~ s/^x-//;
  6966. $$self{VALUE}{MIMEType} = "$c/$b-$d";
  6967. $self->VPrint(0, " Modified MIMEType = $c/$b-$d\n");
  6968. } else {
  6969. $self->FoundTag('MIMEType', $mime);
  6970. }
  6971. }
  6972. #------------------------------------------------------------------------------
  6973. # Print verbose output
  6974. # Inputs: 0) ExifTool ref, 1) verbose level (prints if level > this), 2-N) print args
  6975. sub VPrint($$@)
  6976. {
  6977. my $self = shift;
  6978. my $level = shift;
  6979. if ($$self{OPTIONS}{Verbose} and $$self{OPTIONS}{Verbose} > $level) {
  6980. my $out = $$self{OPTIONS}{TextOut};
  6981. print $out @_;
  6982. print $out "\n" unless $_[-1] =~ /\n$/;
  6983. }
  6984. }
  6985. #------------------------------------------------------------------------------
  6986. # Verbose dump
  6987. # Inputs: 0) ExifTool ref, 1) data ref, 2-N) HexDump options
  6988. sub VerboseDump($$;%)
  6989. {
  6990. my $self = shift;
  6991. my $dataPt = shift;
  6992. if ($$self{OPTIONS}{Verbose} and $$self{OPTIONS}{Verbose} > 2) {
  6993. my %parms = (
  6994. Prefix => $$self{INDENT},
  6995. Out => $$self{OPTIONS}{TextOut},
  6996. MaxLen => $$self{OPTIONS}{Verbose} < 4 ? 96 : undef,
  6997. );
  6998. HexDump($dataPt, undef, %parms, @_);
  6999. }
  7000. }
  7001. #------------------------------------------------------------------------------
  7002. # Print data in hex
  7003. # Inputs: 0) data
  7004. # Returns: hex string
  7005. # (this is a convenience function for use in debugging PrintConv statements)
  7006. sub PrintHex($)
  7007. {
  7008. my $val = shift;
  7009. return join(' ', unpack('H2' x length($val), $val));
  7010. }
  7011. #------------------------------------------------------------------------------
  7012. # Extract binary data from file
  7013. # 0) ExifTool object reference, 1) offset, 2) length, 3) tag name if conditional
  7014. # Returns: binary data, or undef on error
  7015. # Notes: Returns "Binary data #### bytes" instead of data unless tag is
  7016. # specifically requested or the Binary option is set
  7017. sub ExtractBinary($$$;$)
  7018. {
  7019. my ($self, $offset, $length, $tag) = @_;
  7020. my ($isPreview, $buff);
  7021. if ($tag) {
  7022. if ($tag eq 'PreviewImage') {
  7023. # save PreviewImage start/length in case we want to dump trailer
  7024. $$self{PreviewImageStart} = $offset;
  7025. $$self{PreviewImageLength} = $length;
  7026. $isPreview = 1;
  7027. }
  7028. my $lcTag = lc $tag;
  7029. if ((not $$self{OPTIONS}{Binary} or $$self{EXCL_TAG_LOOKUP}{$lcTag}) and
  7030. not $$self{OPTIONS}{Verbose} and not $$self{REQ_TAG_LOOKUP}{$lcTag})
  7031. {
  7032. return "Binary data $length bytes";
  7033. }
  7034. }
  7035. unless ($$self{RAF}->Seek($offset,0)
  7036. and $$self{RAF}->Read($buff, $length) == $length)
  7037. {
  7038. $tag or $tag = 'binary data';
  7039. if ($isPreview and not $$self{BuildingComposite}) {
  7040. $$self{PreviewError} = 1;
  7041. } else {
  7042. $self->Warn("Error reading $tag from file", $isPreview);
  7043. }
  7044. return undef;
  7045. }
  7046. return $buff;
  7047. }
  7048. #------------------------------------------------------------------------------
  7049. # Process binary data
  7050. # Inputs: 0) ExifTool object ref, 1) directory information ref, 2) tag table ref
  7051. # Returns: 1 on success
  7052. # Notes: dirInfo may contain VarFormatData (reference to empty list) to return
  7053. # details about any variable-length-format tags in the table (used when writing)
  7054. sub ProcessBinaryData($$$)
  7055. {
  7056. my ($self, $dirInfo, $tagTablePtr) = @_;
  7057. my $dataPt = $$dirInfo{DataPt};
  7058. my $offset = $$dirInfo{DirStart} || 0;
  7059. my $size = $$dirInfo{DirLen} || (length($$dataPt) - $offset);
  7060. my $base = $$dirInfo{Base} || 0;
  7061. my $verbose = $$self{OPTIONS}{Verbose};
  7062. my $unknown = $$self{OPTIONS}{Unknown};
  7063. my $dataPos = $$dirInfo{DataPos} || 0;
  7064. # get default format ('int8u' unless specified)
  7065. my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
  7066. my $increment = $formatSize{$defaultFormat};
  7067. unless ($increment) {
  7068. warn "Unknown format $defaultFormat\n";
  7069. $defaultFormat = 'int8u';
  7070. $increment = $formatSize{$defaultFormat};
  7071. }
  7072. # prepare list of tag numbers to extract
  7073. my (@tags, $topIndex);
  7074. if ($unknown > 1 and defined $$tagTablePtr{FIRST_ENTRY}) {
  7075. # don't create a stupid number of tags if data is huge
  7076. my $sizeLimit = $size < 65536 ? $size : 65536;
  7077. # scan through entire binary table
  7078. $topIndex = int($sizeLimit/$increment);
  7079. @tags = ($$tagTablePtr{FIRST_ENTRY}..($topIndex - 1));
  7080. # add in floating point tag ID's if they exist
  7081. my @ftags = grep /\./, TagTableKeys($tagTablePtr);
  7082. @tags = sort { $a <=> $b } @tags, @ftags if @ftags;
  7083. } elsif ($$dirInfo{DataMember}) {
  7084. @tags = @{$$dirInfo{DataMember}};
  7085. $verbose = 0; # no verbose output of extracted values when writing
  7086. } elsif ($$dirInfo{MixedTags}) {
  7087. # process sorted integer-ID tags only
  7088. @tags = sort { $a <=> $b } grep /^\d+$/, TagTableKeys($tagTablePtr);
  7089. } else {
  7090. # extract known tags in numerical order
  7091. @tags = sort { ($a < 0 ? $a + 1e9 : $a) <=> ($b < 0 ? $b + 1e9 : $b) } TagTableKeys($tagTablePtr);
  7092. }
  7093. $self->VerboseDir('BinaryData', undef, $size) if $verbose;
  7094. # avoid creating unknown tags for tags that fail condition if Unknown is 1
  7095. $$self{NO_UNKNOWN} = 1 if $unknown < 2;
  7096. my ($index, %val);
  7097. my $nextIndex = 0;
  7098. my $varSize = 0;
  7099. foreach $index (@tags) {
  7100. my ($tagInfo, $val, $saveNextIndex, $len, $mask, $wasVar, $rational);
  7101. if ($$tagTablePtr{$index}) {
  7102. $tagInfo = $self->GetTagInfo($tagTablePtr, $index);
  7103. unless ($tagInfo) {
  7104. next unless defined $tagInfo;
  7105. my $entry = int($index) * $increment + $varSize;
  7106. if ($entry < 0) {
  7107. $entry += $size;
  7108. next if $entry < 0;
  7109. }
  7110. next if $entry >= $size;
  7111. my $more = $size - $entry;
  7112. $more = 128 if $more > 128;
  7113. my $v = substr($$dataPt, $entry+$offset, $more);
  7114. $tagInfo = $self->GetTagInfo($tagTablePtr, $index, \$v);
  7115. next unless $tagInfo;
  7116. }
  7117. next if $$tagInfo{Unknown} and
  7118. ($$tagInfo{Unknown} > $unknown or $index < $nextIndex);
  7119. } elsif ($topIndex and $$tagTablePtr{$index - $topIndex}) {
  7120. $tagInfo = $self->GetTagInfo($tagTablePtr, $index - $topIndex) or next;
  7121. } else {
  7122. # don't generate unknown tags in binary tables unless Unknown > 1
  7123. next unless $unknown > 1;
  7124. next if $index < $nextIndex; # skip if data already used
  7125. $tagInfo = $self->GetTagInfo($tagTablePtr, $index) or next;
  7126. $$tagInfo{Unknown} = 2; # set unknown to 2 for binary unknowns
  7127. }
  7128. # get relative offset of this entry
  7129. my $entry = int($index) * $increment + $varSize;
  7130. # allow negative indices to represent bytes from end
  7131. if ($entry < 0) {
  7132. $entry += $size;
  7133. next if $entry < 0;
  7134. }
  7135. my $more = $size - $entry;
  7136. last if $more <= 0; # all done if we have reached the end of data
  7137. my $count = 1;
  7138. my $format = $$tagInfo{Format};
  7139. if (not $format) {
  7140. $format = $defaultFormat;
  7141. } elsif ($format eq 'string') {
  7142. # string with no specified count runs to end of block
  7143. $count = $more;
  7144. } elsif ($format eq 'pstring') {
  7145. $format = 'string';
  7146. $count = Get8u($dataPt, ($entry++)+$offset);
  7147. --$more;
  7148. } elsif (not $formatSize{$format}) {
  7149. if ($format =~ /(.*)\[(.*)\]/) {
  7150. # handle format count field
  7151. $format = $1;
  7152. $count = $2;
  7153. # evaluate count to allow count to be based on previous values
  7154. #### eval Format size (%val, $size, $self)
  7155. $count = eval $count;
  7156. $@ and warn("Format $$tagInfo{Name}: $@"), next;
  7157. next if $count < 0;
  7158. # allow a variable-length value of any format
  7159. # (note: the next incremental index points to data immediately after
  7160. # this value, regardless of the size of this value, even if it is zero)
  7161. if ($format =~ s/^var_//) {
  7162. $varSize += $count * ($formatSize{$format} || 1) - $increment;
  7163. $wasVar = 1;
  7164. # save variable size data if required for writing
  7165. if ($$dirInfo{VarFormatData}) {
  7166. push @{$$dirInfo{VarFormatData}}, [ $index, $varSize, $format ];
  7167. }
  7168. # don't extract value if large and we wanted it just to get
  7169. # the variable-format information when writing
  7170. next if $$tagInfo{LargeTag} and $$dirInfo{VarFormatData};
  7171. }
  7172. } elsif ($format =~ /^var_/) {
  7173. # handle variable-length string formats
  7174. $format = substr($format, 4);
  7175. pos($$dataPt) = $entry + $offset;
  7176. undef $count;
  7177. if ($format eq 'ustring') {
  7178. $count = pos($$dataPt) - ($entry+$offset) if $$dataPt =~ /\G(..)*?\0\0/sg;
  7179. $varSize -= 2; # ($count includes base size of 2 bytes)
  7180. } elsif ($format eq 'pstring') {
  7181. $count = Get8u($dataPt, ($entry++)+$offset);
  7182. --$more;
  7183. } elsif ($format eq 'pstr32') {
  7184. last if $more < 4;
  7185. $count = Get32u($dataPt, $entry + $offset);
  7186. $entry += 4;
  7187. $more -= 4;
  7188. } elsif ($format eq 'int16u') {
  7189. # int16u size of binary data to follow
  7190. last if $more < 2;
  7191. $count = Get16u($dataPt, $entry + $offset) + 2;
  7192. $varSize -= 2; # ($count includes size word)
  7193. $format = 'undef';
  7194. } elsif ($$dataPt =~ /\0/g) {
  7195. $count = pos($$dataPt) - ($entry+$offset);
  7196. --$varSize; # ($count includes base size of 1 byte)
  7197. }
  7198. $count = $more if not defined $count or $count > $more;
  7199. $varSize += $count; # shift subsequent indices
  7200. $val = substr($$dataPt, $entry+$offset, $count);
  7201. $val = $self->Decode($val, 'UCS2') if $format eq 'ustring';
  7202. $val =~ s/\0.*//s unless $format eq 'undef'; # truncate at null
  7203. $wasVar = 1;
  7204. # save variable size data if required for writing
  7205. if ($$dirInfo{VarFormatData}) {
  7206. push @{$$dirInfo{VarFormatData}}, [ $index, $varSize, $format ];
  7207. }
  7208. }
  7209. }
  7210. # hook to allow format, etc to be set dynamically
  7211. if (defined $$tagInfo{Hook}) {
  7212. #### eval Hook ($format, $varSize, $size)
  7213. eval $$tagInfo{Hook};
  7214. # save variable size data if required for writing (in case changed by Hook)
  7215. if ($$dirInfo{VarFormatData}) {
  7216. $#{$$dirInfo{VarFormatData}} -= 1 if $wasVar; # remove previous entry for this tag
  7217. push @{$$dirInfo{VarFormatData}}, [ $index, $varSize, $format ];
  7218. }
  7219. }
  7220. if ($unknown > 1) {
  7221. # calculate next valid index for unknown tag
  7222. my $ni = int $index;
  7223. $ni += (($formatSize{$format} || 1) * $count) / $increment unless $wasVar;
  7224. $saveNextIndex = $nextIndex;
  7225. $nextIndex = $ni unless $nextIndex > $ni;
  7226. }
  7227. # allow large tags to be excluded from extraction
  7228. # (provides a work-around for some tight memory situations)
  7229. next if $$tagInfo{LargeTag} and $$self{EXCL_TAG_LOOKUP}{lc $$tagInfo{Name}};
  7230. # read value now if necessary
  7231. unless (defined $val and not $$tagInfo{SubDirectory}) {
  7232. $val = ReadValue($dataPt, $entry+$offset, $format, $count, $more, \$rational);
  7233. $mask = $$tagInfo{Mask};
  7234. $val &= $mask if $mask;
  7235. }
  7236. if ($verbose and not $$tagInfo{Hidden}) {
  7237. if (not $$tagInfo{SubDirectory} or $$tagInfo{Format}) {
  7238. $len = $count * ($formatSize{$format} || 1);
  7239. $len = $more if $len > $more;
  7240. } else {
  7241. $len = $more;
  7242. }
  7243. $self->VerboseInfo($index, $tagInfo,
  7244. Table => $tagTablePtr,
  7245. Value => $val,
  7246. DataPt => $dataPt,
  7247. Size => $len,
  7248. Start => $entry+$offset,
  7249. Addr => $entry+$offset+$base+$dataPos,
  7250. Format => $format,
  7251. Count => $count,
  7252. Extra => $mask ? sprintf(', mask 0x%.2x',$mask) : undef,
  7253. );
  7254. }
  7255. # parse nested BinaryData directories
  7256. if ($$tagInfo{SubDirectory}) {
  7257. my $subdir = $$tagInfo{SubDirectory};
  7258. my $subTablePtr = GetTagTable($$subdir{TagTable});
  7259. # use specified subdirectory length if given
  7260. if ($$tagInfo{Format} and $formatSize{$format}) {
  7261. $len = $count * $formatSize{$format};
  7262. $len = $more if $len > $more;
  7263. } else {
  7264. $len = $more; # directory size is all of remaining data
  7265. if ($$subTablePtr{PROCESS_PROC} and
  7266. $$subTablePtr{PROCESS_PROC} eq \&ProcessBinaryData)
  7267. {
  7268. # the rest of the data will be printed in the subdirectory
  7269. $nextIndex = $size / $increment;
  7270. }
  7271. }
  7272. my $subdirBase = $base;
  7273. if (defined $$subdir{Base}) {
  7274. #### eval Base ($start,$base)
  7275. my $start = $entry + $offset + $dataPos;
  7276. $subdirBase = eval($$subdir{Base}) + $base;
  7277. }
  7278. my $start = $$subdir{Start} || 0;
  7279. my %subdirInfo = (
  7280. DataPt => $dataPt,
  7281. DataPos => $dataPos,
  7282. DataLen => length $$dataPt,
  7283. DirStart => $entry + $offset + $start,
  7284. DirLen => $len - $start,
  7285. Base => $subdirBase,
  7286. );
  7287. delete $$self{NO_UNKNOWN};
  7288. $self->ProcessDirectory(\%subdirInfo, $subTablePtr, $$subdir{ProcessProc});
  7289. $$self{NO_UNKNOWN} = 1 if $unknown < 2;
  7290. next;
  7291. }
  7292. if ($$tagInfo{IsOffset} and $$tagInfo{IsOffset} ne '3') {
  7293. my $et = $self;
  7294. #### eval IsOffset ($val, $et)
  7295. $val += $base + $$self{BASE} if eval $$tagInfo{IsOffset};
  7296. }
  7297. $val{$index} = $val;
  7298. my $oldBase;
  7299. if ($$tagInfo{SetBase}) {
  7300. $oldBase = $$self{BASE};
  7301. $$self{BASE} += $base;
  7302. }
  7303. my $key = $self->FoundTag($tagInfo,$val);
  7304. $$self{BASE} = $oldBase if defined $oldBase;
  7305. if ($key) {
  7306. $$self{RATIONAL}{$key} = $rational if defined $rational;
  7307. } else {
  7308. # don't increment nextIndex if we didn't extract a tag
  7309. $nextIndex = $saveNextIndex if defined $saveNextIndex;
  7310. }
  7311. }
  7312. delete $$self{NO_UNKNOWN};
  7313. return 1;
  7314. }
  7315. #..............................................................................
  7316. # Load .ExifTool_config file from user's home directory
  7317. # (use of noConfig is now deprecated, use configFile = '' instead)
  7318. until ($Image::ExifTool::noConfig) {
  7319. my $file = $Image::ExifTool::configFile;
  7320. if (not defined $file) {
  7321. my $config = '.ExifTool_config';
  7322. # get our home directory (HOMEDRIVE and HOMEPATH are used in Windows cmd shell)
  7323. my $home = $ENV{EXIFTOOL_HOME} || $ENV{HOME} ||
  7324. ($ENV{HOMEDRIVE} || '') . ($ENV{HOMEPATH} || '') || '.';
  7325. # look for the config file in 1) the home directory, 2) the program dir
  7326. $file = "$home/$config";
  7327. -r $file or $file = ($0 =~ /(.*[\\\/])/ ? $1 : './') . $config;
  7328. -r $file or last;
  7329. } else {
  7330. length $file or last; # filename of "" disables configuration
  7331. -r $file or warn("Config file not found\n"), last;
  7332. }
  7333. eval { require $file }; # load the config file
  7334. # print warning (minus "Compilation failed" part)
  7335. $@ and $_=$@, s/Compilation failed.*//s, warn $_;
  7336. last;
  7337. }
  7338. # read user-defined lenses (may have been defined by script instead of config file)
  7339. if (@Image::ExifTool::UserDefined::Lenses) {
  7340. foreach (@Image::ExifTool::UserDefined::Lenses) {
  7341. $Image::ExifTool::userLens{$_} = 1;
  7342. }
  7343. }
  7344. # add user-defined file types
  7345. if (%Image::ExifTool::UserDefined::FileTypes) {
  7346. foreach (sort keys %Image::ExifTool::UserDefined::FileTypes) {
  7347. my $fileInfo = $Image::ExifTool::UserDefined::FileTypes{$_};
  7348. my $type = uc $_;
  7349. ref $fileInfo eq 'HASH' or $fileTypeLookup{$type} = $fileInfo, next;
  7350. if ($$fileInfo{BaseType}) {
  7351. if ($$fileInfo{Description}) {
  7352. $fileTypeLookup{$type} = [ $$fileInfo{BaseType}, $$fileInfo{Description} ];
  7353. } else {
  7354. $fileTypeLookup{$type} = $$fileInfo{BaseType};
  7355. }
  7356. } else {
  7357. $fileTypeLookup{$type} = [ $type, $$fileInfo{Description} || $type ];
  7358. $moduleName{$type} = 0; # not supported
  7359. if ($$fileInfo{Magic}) {
  7360. $magicNumber{$type} = $$fileInfo{Magic};
  7361. push @fileTypes, $type unless grep /^$type$/, @fileTypes;
  7362. }
  7363. }
  7364. $mimeType{$type} = $$fileInfo{MIMEType} if defined $$fileInfo{MIMEType};
  7365. }
  7366. }
  7367. #------------------------------------------------------------------------------
  7368. 1; # end