CanonVRD.pm 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273
  1. #------------------------------------------------------------------------------
  2. # File: CanonVRD.pm
  3. #
  4. # Description: Read/write Canon VRD and DR4 information
  5. #
  6. # Revisions: 2006/10/30 - P. Harvey Created
  7. # 2007/10/23 - PH Added new VRD 3.0 tags
  8. # 2008/08/29 - PH Added new VRD 3.4 tags
  9. # 2008/12/02 - PH Added new VRD 3.5 tags
  10. # 2010/06/18 - PH Support variable-length CustomPictureStyle data
  11. # 2010/09/14 - PH Added r/w support for XMP in VRD
  12. # 2015/05/16 - PH Added DR4 support (DPP 4.1.50.0)
  13. #
  14. # References: 1) Bogdan private communication (Canon DPP v3.4.1.1)
  15. # 2) Gert Kello private communiation (DPP 3.8)
  16. #------------------------------------------------------------------------------
  17. package Image::ExifTool::CanonVRD;
  18. use strict;
  19. use vars qw($VERSION);
  20. use Image::ExifTool qw(:DataAccess :Utils);
  21. use Image::ExifTool::Canon;
  22. $VERSION = '1.28';
  23. sub ProcessCanonVRD($$;$);
  24. sub WriteCanonVRD($$;$);
  25. sub ProcessEditData($$$);
  26. sub ProcessIHL($$$);
  27. sub ProcessIHLExif($$$);
  28. sub ProcessDR4($$;$);
  29. sub SortDR4($$);
  30. # map for adding directories to VRD
  31. my %vrdMap = (
  32. XMP => 'CanonVRD',
  33. CanonVRD => 'VRD',
  34. );
  35. my %noYes = (
  36. PrintConvColumns => 2,
  37. PrintConv => { 0 => 'No', 1 => 'Yes' },
  38. );
  39. # DR4 format codes
  40. my %vrdFormat = (
  41. 1 => 'int32u',
  42. 2 => 'string',
  43. 8 => 'int32u',
  44. 9 => 'int32s',
  45. 13 => 'double',
  46. 33 => 'int32u', # (array)
  47. 38 => 'double', # (array)
  48. # 254 => 'undef', ?
  49. 255 => 'undef',
  50. );
  51. # empty VRD header/footer for creating VRD from scratch
  52. my $blankHeader = "CANON OPTIONAL DATA\0\0\x01\0\0\0\0\0\0";
  53. my $blankFooter = "CANON OPTIONAL DATA\0" . ("\0" x 42) . "\xff\xd9";
  54. # main tag table blocks in CanonVRD trailer (ref PH)
  55. %Image::ExifTool::CanonVRD::Main = (
  56. WRITE_PROC => \&WriteCanonVRD,
  57. PROCESS_PROC => \&ProcessCanonVRD,
  58. NOTES => q{
  59. Canon Digital Photo Professional writes VRD (Recipe Data) information as a
  60. trailer record to JPEG, TIFF, CRW and CR2 images, or as stand-alone VRD or
  61. DR4 files. The tags listed below represent information found in these
  62. records. The complete VRD/DR4 data record may be accessed as a block using
  63. the Extra 'CanonVRD' or 'CanonDR4' tag, but this tag is not extracted or
  64. copied unless specified explicitly.
  65. },
  66. 0xffff00f4 => {
  67. Name => 'EditData',
  68. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Edit' },
  69. },
  70. 0xffff00f5 => {
  71. Name => 'IHLData',
  72. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::IHL' },
  73. },
  74. 0xffff00f6 => {
  75. Name => 'XMP',
  76. Flags => [ 'Binary', 'Protected' ],
  77. Writable => 'undef', # allow writing/deleting as a block
  78. SubDirectory => {
  79. DirName => 'XMP',
  80. TagTable => 'Image::ExifTool::XMP::Main',
  81. },
  82. },
  83. 0xffff00f7 => {
  84. Name => 'Edit4Data',
  85. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Edit4' },
  86. },
  87. );
  88. # the VRD edit information is divided into sections
  89. %Image::ExifTool::CanonVRD::Edit = (
  90. WRITE_PROC => \&ProcessEditData,
  91. PROCESS_PROC => \&ProcessEditData,
  92. VARS => { ID_LABEL => 'Index' }, # change TagID label in documentation
  93. NOTES => 'Canon VRD edit information.',
  94. 0 => {
  95. Name => 'VRD1',
  96. Size => 0x272, # size of version 1.0 edit information in bytes
  97. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver1' },
  98. },
  99. 1 => {
  100. Name => 'VRDStampTool',
  101. Size => 0, # size is variable, and obtained from int32u at directory start
  102. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampTool' },
  103. },
  104. 2 => {
  105. Name => 'VRD2',
  106. Size => undef, # size is the remaining edit data
  107. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver2' },
  108. },
  109. );
  110. # Canon DPP version 4 edit information
  111. %Image::ExifTool::CanonVRD::Edit4 = (
  112. WRITE_PROC => \&ProcessEditData,
  113. PROCESS_PROC => \&ProcessEditData,
  114. VARS => { ID_LABEL => 'Index' }, # change TagID label in documentation
  115. NOTES => 'Canon DPP version 4 edit information.',
  116. 0 => {
  117. Name => 'DR4',
  118. Size => undef, # size is the remaining edit data
  119. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4' },
  120. },
  121. );
  122. # "IHL Created Optional Item Data" tags (not yet writable)
  123. %Image::ExifTool::CanonVRD::IHL = (
  124. PROCESS_PROC => \&ProcessIHL,
  125. TAG_PREFIX => 'VRD_IHL',
  126. GROUPS => { 2 => 'Image' },
  127. 1 => [
  128. # this contains edited TIFF-format data, with an original IFD at 0x0008
  129. # and an edited IFD with offset given in the TIFF header.
  130. {
  131. Name => 'IHL_EXIF',
  132. Condition => '$self->Options("ExtractEmbedded")',
  133. SubDirectory => {
  134. TagTable => 'Image::ExifTool::Exif::Main',
  135. ProcessProc => \&ProcessIHLExif,
  136. },
  137. },{
  138. Name => 'IHL_EXIF',
  139. Notes => q{
  140. extracted as a block if the Unknown option is used, or processed as the
  141. first sub-document with the ExtractEmbedded option
  142. },
  143. Binary => 1,
  144. Unknown => 1,
  145. },
  146. ],
  147. # 2 - written by DPP 3.0.2.6, and it looks something like edit data,
  148. # but I haven't decoded it yet - PH
  149. 3 => {
  150. # (same size as the PreviewImage with DPP 3.0.2.6)
  151. Name => 'ThumbnailImage',
  152. Groups => { 2 => 'Preview' },
  153. Binary => 1,
  154. },
  155. 4 => {
  156. Name => 'PreviewImage',
  157. Groups => { 2 => 'Preview' },
  158. Binary => 1,
  159. },
  160. 5 => {
  161. Name => 'RawCodecVersion',
  162. ValueConv => '$val =~ s/\0.*//s; $val', # truncate string at null
  163. },
  164. 6 => {
  165. Name => 'CRCDevelParams',
  166. Binary => 1,
  167. Unknown => 1,
  168. },
  169. );
  170. # VRD version 1 tags (ref PH)
  171. %Image::ExifTool::CanonVRD::Ver1 = (
  172. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  173. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  174. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  175. WRITABLE => 1,
  176. FIRST_ENTRY => 0,
  177. GROUPS => { 2 => 'Image' },
  178. DATAMEMBER => [ 0x002 ], # necessary for writing
  179. #
  180. # RAW image adjustment
  181. #
  182. 0x002 => {
  183. Name => 'VRDVersion',
  184. Format => 'int16u',
  185. Writable => 0,
  186. DataMember => 'VRDVersion',
  187. RawConv => '$$self{VRDVersion} = $val',
  188. PrintConv => '$val =~ s/^(\d)(\d*)(\d)$/$1.$2.$3/; $val',
  189. },
  190. 0x006 => {
  191. Name => 'WBAdjRGGBLevels',
  192. Format => 'int16u[4]',
  193. },
  194. 0x018 => {
  195. Name => 'WhiteBalanceAdj',
  196. Format => 'int16u',
  197. PrintConvColumns => 2,
  198. PrintConv => {
  199. 0 => 'Auto',
  200. 1 => 'Daylight',
  201. 2 => 'Cloudy',
  202. 3 => 'Tungsten',
  203. 4 => 'Fluorescent',
  204. 5 => 'Flash',
  205. 8 => 'Shade',
  206. 9 => 'Kelvin',
  207. 30 => 'Manual (Click)',
  208. 31 => 'Shot Settings',
  209. },
  210. },
  211. 0x01a => {
  212. Name => 'WBAdjColorTemp',
  213. Format => 'int16u',
  214. },
  215. # 0x01c similar to 0x006
  216. 0x024 => {
  217. Name => 'WBFineTuneActive',
  218. Format => 'int16u',
  219. %noYes,
  220. },
  221. 0x028 => {
  222. Name => 'WBFineTuneSaturation',
  223. Format => 'int16u',
  224. },
  225. 0x02c => {
  226. Name => 'WBFineTuneTone',
  227. Format => 'int16u',
  228. },
  229. 0x02e => {
  230. Name => 'RawColorAdj',
  231. Format => 'int16u',
  232. PrintConv => {
  233. 0 => 'Shot Settings',
  234. 1 => 'Faithful',
  235. 2 => 'Custom',
  236. },
  237. },
  238. 0x030 => {
  239. Name => 'RawCustomSaturation',
  240. Format => 'int32s',
  241. },
  242. 0x034 => {
  243. Name => 'RawCustomTone',
  244. Format => 'int32s',
  245. },
  246. 0x038 => {
  247. Name => 'RawBrightnessAdj',
  248. Format => 'int32s',
  249. ValueConv => '$val / 6000',
  250. ValueConvInv => 'int($val * 6000 + ($val < 0 ? -0.5 : 0.5))',
  251. PrintConv => 'sprintf("%.2f",$val)',
  252. PrintConvInv => '$val',
  253. },
  254. 0x03c => {
  255. Name => 'ToneCurveProperty',
  256. Format => 'int16u',
  257. PrintConvColumns => 2,
  258. PrintConv => {
  259. 0 => 'Shot Settings',
  260. 1 => 'Linear',
  261. 2 => 'Custom 1',
  262. 3 => 'Custom 2',
  263. 4 => 'Custom 3',
  264. 5 => 'Custom 4',
  265. 6 => 'Custom 5',
  266. },
  267. },
  268. # 0x040 usually "10 9 2"
  269. 0x07a => {
  270. Name => 'DynamicRangeMin',
  271. Format => 'int16u',
  272. },
  273. 0x07c => {
  274. Name => 'DynamicRangeMax',
  275. Format => 'int16u',
  276. },
  277. # 0x0c6 usually "10 9 2"
  278. #
  279. # RGB image adjustment
  280. #
  281. 0x110 => {
  282. Name => 'ToneCurveActive',
  283. Format => 'int16u',
  284. %noYes,
  285. },
  286. 0x113 => {
  287. Name => 'ToneCurveMode',
  288. PrintConv => { 0 => 'RGB', 1 => 'Luminance' },
  289. },
  290. 0x114 => {
  291. Name => 'BrightnessAdj',
  292. Format => 'int8s',
  293. },
  294. 0x115 => {
  295. Name => 'ContrastAdj',
  296. Format => 'int8s',
  297. },
  298. 0x116 => {
  299. Name => 'SaturationAdj',
  300. Format => 'int16s',
  301. },
  302. 0x11e => {
  303. Name => 'ColorToneAdj',
  304. Notes => 'in degrees, so -1 is the same as 359',
  305. Format => 'int32s',
  306. },
  307. 0x126 => {
  308. Name => 'LuminanceCurvePoints',
  309. Format => 'int16u[21]',
  310. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  311. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  312. },
  313. 0x150 => {
  314. Name => 'LuminanceCurveLimits',
  315. Notes => '4 numbers: input and output highlight and shadow points',
  316. Format => 'int16u[4]',
  317. },
  318. 0x159 => {
  319. Name => 'ToneCurveInterpolation',
  320. PrintConv => { 0 => 'Curve', 1 => 'Straight' },
  321. },
  322. 0x160 => {
  323. Name => 'RedCurvePoints',
  324. Format => 'int16u[21]',
  325. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  326. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  327. },
  328. # 0x193 same as 0x159
  329. 0x19a => {
  330. Name => 'GreenCurvePoints',
  331. Format => 'int16u[21]',
  332. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  333. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  334. },
  335. # 0x1cd same as 0x159
  336. 0x1d4 => {
  337. Name => 'BlueCurvePoints',
  338. Format => 'int16u[21]',
  339. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  340. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  341. },
  342. 0x18a => {
  343. Name => 'RedCurveLimits',
  344. Format => 'int16u[4]',
  345. },
  346. 0x1c4 => {
  347. Name => 'GreenCurveLimits',
  348. Format => 'int16u[4]',
  349. },
  350. 0x1fe => {
  351. Name => 'BlueCurveLimits',
  352. Format => 'int16u[4]',
  353. },
  354. # 0x207 same as 0x159
  355. 0x20e => {
  356. Name => 'RGBCurvePoints',
  357. Format => 'int16u[21]',
  358. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  359. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  360. },
  361. 0x238 => {
  362. Name => 'RGBCurveLimits',
  363. Format => 'int16u[4]',
  364. },
  365. # 0x241 same as 0x159
  366. 0x244 => {
  367. Name => 'CropActive',
  368. Format => 'int16u',
  369. %noYes,
  370. },
  371. 0x246 => {
  372. Name => 'CropLeft',
  373. Notes => 'crop coordinates in original unrotated image',
  374. Format => 'int16u',
  375. },
  376. 0x248 => {
  377. Name => 'CropTop',
  378. Format => 'int16u',
  379. },
  380. 0x24a => {
  381. Name => 'CropWidth',
  382. Format => 'int16u',
  383. },
  384. 0x24c => {
  385. Name => 'CropHeight',
  386. Format => 'int16u',
  387. },
  388. 0x25a => {
  389. Name => 'SharpnessAdj',
  390. Format => 'int16u',
  391. },
  392. 0x260 => {
  393. Name => 'CropAspectRatio',
  394. Format => 'int16u',
  395. PrintConv => {
  396. 0 => 'Free',
  397. 1 => '3:2',
  398. 2 => '2:3',
  399. 3 => '4:3',
  400. 4 => '3:4',
  401. 5 => 'A-size Landscape',
  402. 6 => 'A-size Portrait',
  403. 7 => 'Letter-size Landscape',
  404. 8 => 'Letter-size Portrait',
  405. 9 => '4:5',
  406. 10 => '5:4',
  407. 11 => '1:1',
  408. 12 => 'Circle',
  409. 65535 => 'Custom',
  410. },
  411. },
  412. 0x262 => {
  413. Name => 'ConstrainedCropWidth',
  414. Format => 'float',
  415. PrintConv => 'sprintf("%.7g",$val)',
  416. PrintConvInv => '$val',
  417. },
  418. 0x266 => {
  419. Name => 'ConstrainedCropHeight',
  420. Format => 'float',
  421. PrintConv => 'sprintf("%.7g",$val)',
  422. PrintConvInv => '$val',
  423. },
  424. 0x26a => {
  425. Name => 'CheckMark',
  426. Format => 'int16u',
  427. PrintConv => {
  428. 0 => 'Clear',
  429. 1 => 1,
  430. 2 => 2,
  431. 3 => 3,
  432. },
  433. },
  434. 0x26e => {
  435. Name => 'Rotation',
  436. Format => 'int16u',
  437. PrintConv => {
  438. 0 => 0,
  439. 1 => 90,
  440. 2 => 180,
  441. 3 => 270,
  442. },
  443. },
  444. 0x270 => {
  445. Name => 'WorkColorSpace',
  446. Format => 'int16u',
  447. PrintConv => {
  448. 0 => 'sRGB',
  449. 1 => 'Adobe RGB',
  450. 2 => 'Wide Gamut RGB',
  451. 3 => 'Apple RGB',
  452. 4 => 'ColorMatch RGB',
  453. },
  454. },
  455. # (VRD 1.0.0 edit data ends here -- 0x272 bytes)
  456. );
  457. # VRD Stamp Tool tags (ref PH)
  458. %Image::ExifTool::CanonVRD::StampTool = (
  459. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  460. GROUPS => { 2 => 'Image' },
  461. 0x00 => {
  462. Name => 'StampToolCount',
  463. Format => 'int32u',
  464. },
  465. );
  466. # VRD version 2 and 3 tags (ref PH)
  467. %Image::ExifTool::CanonVRD::Ver2 = (
  468. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  469. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  470. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  471. WRITABLE => 1,
  472. FIRST_ENTRY => 0,
  473. FORMAT => 'int16s',
  474. DATAMEMBER => [ 0x58, 0xdc, 0xdf, 0xe0 ], # (required for DataMember and var-format tags)
  475. IS_SUBDIR => [ 0xe0 ],
  476. GROUPS => { 2 => 'Image' },
  477. NOTES => 'Tags added in DPP version 2.0 and later.',
  478. 0x02 => {
  479. Name => 'PictureStyle',
  480. PrintConvColumns => 2,
  481. PrintConv => {
  482. 0 => 'Standard',
  483. 1 => 'Portrait',
  484. 2 => 'Landscape',
  485. 3 => 'Neutral',
  486. 4 => 'Faithful',
  487. 5 => 'Monochrome',
  488. 6 => 'Unknown?', # PH (maybe in-camera custom picture style?)
  489. 7 => 'Custom',
  490. },
  491. },
  492. 0x03 => { Name => 'IsCustomPictureStyle', %noYes },
  493. # 0x08: 3
  494. # 0x09: 4095
  495. # 0x0a: 0
  496. # 0x0b: 4095
  497. # 0x0c: 0
  498. 0x0d => 'StandardRawColorTone',
  499. 0x0e => 'StandardRawSaturation',
  500. 0x0f => 'StandardRawContrast',
  501. 0x10 => { Name => 'StandardRawLinear', %noYes },
  502. 0x11 => 'StandardRawSharpness',
  503. 0x12 => 'StandardRawHighlightPoint',
  504. 0x13 => 'StandardRawShadowPoint',
  505. 0x14 => 'StandardOutputHighlightPoint', #2
  506. 0x15 => 'StandardOutputShadowPoint', #2
  507. 0x16 => 'PortraitRawColorTone',
  508. 0x17 => 'PortraitRawSaturation',
  509. 0x18 => 'PortraitRawContrast',
  510. 0x19 => { Name => 'PortraitRawLinear', %noYes },
  511. 0x1a => 'PortraitRawSharpness',
  512. 0x1b => 'PortraitRawHighlightPoint',
  513. 0x1c => 'PortraitRawShadowPoint',
  514. 0x1d => 'PortraitOutputHighlightPoint',
  515. 0x1e => 'PortraitOutputShadowPoint',
  516. 0x1f => 'LandscapeRawColorTone',
  517. 0x20 => 'LandscapeRawSaturation',
  518. 0x21 => 'LandscapeRawContrast',
  519. 0x22 => { Name => 'LandscapeRawLinear', %noYes },
  520. 0x23 => 'LandscapeRawSharpness',
  521. 0x24 => 'LandscapeRawHighlightPoint',
  522. 0x25 => 'LandscapeRawShadowPoint',
  523. 0x26 => 'LandscapeOutputHighlightPoint',
  524. 0x27 => 'LandscapeOutputShadowPoint',
  525. 0x28 => 'NeutralRawColorTone',
  526. 0x29 => 'NeutralRawSaturation',
  527. 0x2a => 'NeutralRawContrast',
  528. 0x2b => { Name => 'NeutralRawLinear', %noYes },
  529. 0x2c => 'NeutralRawSharpness',
  530. 0x2d => 'NeutralRawHighlightPoint',
  531. 0x2e => 'NeutralRawShadowPoint',
  532. 0x2f => 'NeutralOutputHighlightPoint',
  533. 0x30 => 'NeutralOutputShadowPoint',
  534. 0x31 => 'FaithfulRawColorTone',
  535. 0x32 => 'FaithfulRawSaturation',
  536. 0x33 => 'FaithfulRawContrast',
  537. 0x34 => { Name => 'FaithfulRawLinear', %noYes },
  538. 0x35 => 'FaithfulRawSharpness',
  539. 0x36 => 'FaithfulRawHighlightPoint',
  540. 0x37 => 'FaithfulRawShadowPoint',
  541. 0x38 => 'FaithfulOutputHighlightPoint',
  542. 0x39 => 'FaithfulOutputShadowPoint',
  543. 0x3a => {
  544. Name => 'MonochromeFilterEffect',
  545. PrintConv => {
  546. -2 => 'None',
  547. -1 => 'Yellow',
  548. 0 => 'Orange',
  549. 1 => 'Red',
  550. 2 => 'Green',
  551. },
  552. },
  553. 0x3b => {
  554. Name => 'MonochromeToningEffect',
  555. PrintConv => {
  556. -2 => 'None',
  557. -1 => 'Sepia',
  558. 0 => 'Blue',
  559. 1 => 'Purple',
  560. 2 => 'Green',
  561. },
  562. },
  563. 0x3c => 'MonochromeContrast',
  564. 0x3d => { Name => 'MonochromeLinear', %noYes },
  565. 0x3e => 'MonochromeSharpness',
  566. 0x3f => 'MonochromeRawHighlightPoint',
  567. 0x40 => 'MonochromeRawShadowPoint',
  568. 0x41 => 'MonochromeOutputHighlightPoint',
  569. 0x42 => 'MonochromeOutputShadowPoint',
  570. 0x45 => { Name => 'UnknownContrast', Unknown => 1 },
  571. 0x46 => { Name => 'UnknownLinear', %noYes, Unknown => 1 },
  572. 0x47 => { Name => 'UnknownSharpness', Unknown => 1 },
  573. 0x48 => { Name => 'UnknownRawHighlightPoint', Unknown => 1 },
  574. 0x49 => { Name => 'UnknownRawShadowPoint', Unknown => 1 },
  575. 0x4a => { Name => 'UnknownOutputHighlightPoint',Unknown => 1 },
  576. 0x4b => { Name => 'UnknownOutputShadowPoint', Unknown => 1 },
  577. 0x4c => 'CustomColorTone',
  578. 0x4d => 'CustomSaturation',
  579. 0x4e => 'CustomContrast',
  580. 0x4f => { Name => 'CustomLinear', %noYes },
  581. 0x50 => 'CustomSharpness',
  582. 0x51 => 'CustomRawHighlightPoint',
  583. 0x52 => 'CustomRawShadowPoint',
  584. 0x53 => 'CustomOutputHighlightPoint',
  585. 0x54 => 'CustomOutputShadowPoint',
  586. 0x58 => {
  587. Name => 'CustomPictureStyleData',
  588. Format => 'var_int16u',
  589. Binary => 1,
  590. Notes => 'variable-length data structure',
  591. Writable => 0,
  592. RawConv => 'length($val) == 2 ? undef : $val', # ignore if no data
  593. },
  594. # (VRD 2.0.0 edit data ends here: 178 bytes, index 0x59)
  595. 0x5e => [{
  596. Name => 'ChrominanceNoiseReduction',
  597. Condition => '$$self{VRDVersion} < 330',
  598. Notes => 'VRDVersion prior to 3.3.0',
  599. PrintConv => {
  600. 0 => 'Off',
  601. 58 => 'Low',
  602. 100 => 'High',
  603. },
  604. },{ #1
  605. Name => 'ChrominanceNoiseReduction',
  606. Notes => 'VRDVersion 3.3.0 or later',
  607. PrintHex => 1,
  608. PrintConvColumns => 4,
  609. PrintConv => {
  610. 0x00 => 0,
  611. 0x10 => 1,
  612. 0x21 => 2,
  613. 0x32 => 3,
  614. 0x42 => 4,
  615. 0x53 => 5,
  616. 0x64 => 6,
  617. 0x74 => 7,
  618. 0x85 => 8,
  619. 0x96 => 9,
  620. 0xa6 => 10,
  621. 0xa7 => 11,
  622. 0xa8 => 12,
  623. 0xa9 => 13,
  624. 0xaa => 14,
  625. 0xab => 15,
  626. 0xac => 16,
  627. 0xad => 17,
  628. 0xae => 18,
  629. 0xaf => 19,
  630. 0xb0 => 20,
  631. },
  632. }],
  633. 0x5f => [{
  634. Name => 'LuminanceNoiseReduction',
  635. Condition => '$$self{VRDVersion} < 330',
  636. Notes => 'VRDVersion prior to 3.3.0',
  637. PrintConv => {
  638. 0 => 'Off',
  639. 65 => 'Low',
  640. 100 => 'High',
  641. },
  642. },{ #1
  643. Name => 'LuminanceNoiseReduction',
  644. Notes => 'VRDVersion 3.3.0 or later',
  645. PrintHex => 1,
  646. PrintConvColumns => 4,
  647. PrintConv => {
  648. 0x00 => 0,
  649. 0x41 => 1,
  650. 0x64 => 2,
  651. 0x6e => 3,
  652. 0x78 => 4,
  653. 0x82 => 5,
  654. 0x8c => 6,
  655. 0x96 => 7,
  656. 0xa0 => 8,
  657. 0xaa => 9,
  658. 0xb4 => 10,
  659. 0xb5 => 11,
  660. 0xb6 => 12,
  661. 0xb7 => 13,
  662. 0xb8 => 14,
  663. 0xb9 => 15,
  664. 0xba => 16,
  665. 0xbb => 17,
  666. 0xbc => 18,
  667. 0xbd => 19,
  668. 0xbe => 20,
  669. },
  670. }],
  671. 0x60 => [{
  672. Name => 'ChrominanceNR_TIFF_JPEG',
  673. Condition => '$$self{VRDVersion} < 330',
  674. Notes => 'VRDVersion prior to 3.3.0',
  675. PrintConv => {
  676. 0 => 'Off',
  677. 33 => 'Low',
  678. 100 => 'High',
  679. },
  680. },{ #1
  681. Name => 'ChrominanceNR_TIFF_JPEG',
  682. Notes => 'VRDVersion 3.3.0 or later',
  683. PrintHex => 1,
  684. PrintConvColumns => 4,
  685. PrintConv => {
  686. 0x00 => 0,
  687. 0x10 => 1,
  688. 0x21 => 2,
  689. 0x32 => 3,
  690. 0x42 => 4,
  691. 0x53 => 5,
  692. 0x64 => 6,
  693. 0x74 => 7,
  694. 0x85 => 8,
  695. 0x96 => 9,
  696. 0xa6 => 10,
  697. 0xa7 => 11,
  698. 0xa8 => 12,
  699. 0xa9 => 13,
  700. 0xaa => 14,
  701. 0xab => 15,
  702. 0xac => 16,
  703. 0xad => 17,
  704. 0xae => 18,
  705. 0xaf => 19,
  706. 0xb0 => 20,
  707. },
  708. }],
  709. # 0x61: 1
  710. # (VRD 3.0.0 edit data ends here: 196 bytes, index 0x62)
  711. 0x62 => { Name => 'ChromaticAberrationOn', %noYes },
  712. 0x63 => { Name => 'DistortionCorrectionOn', %noYes },
  713. 0x64 => { Name => 'PeripheralIlluminationOn', %noYes },
  714. 0x65 => { Name => 'ColorBlur', %noYes },
  715. 0x66 => {
  716. Name => 'ChromaticAberration',
  717. ValueConv => '$val / 0x400',
  718. ValueConvInv => 'int($val * 0x400 + 0.5)',
  719. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  720. PrintConvInv => 'ToFloat($val) / 100',
  721. },
  722. 0x67 => {
  723. Name => 'DistortionCorrection',
  724. ValueConv => '$val / 0x400',
  725. ValueConvInv => 'int($val * 0x400 + 0.5)',
  726. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  727. PrintConvInv => 'ToFloat($val) / 100',
  728. },
  729. 0x68 => {
  730. Name => 'PeripheralIllumination',
  731. ValueConv => '$val / 0x400',
  732. ValueConvInv => 'int($val * 0x400 + 0.5)',
  733. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  734. PrintConvInv => 'ToFloat($val) / 100',
  735. },
  736. 0x69 => {
  737. Name => 'AberrationCorrectionDistance',
  738. Notes => '100% = infinity',
  739. RawConv => '$val == 0x7fff ? undef : $val',
  740. ValueConv => '1 - $val / 0x400',
  741. ValueConvInv => 'int((1 - $val) * 0x400 + 0.5)',
  742. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  743. PrintConvInv => 'ToFloat($val) / 100',
  744. },
  745. 0x6a => 'ChromaticAberrationRed',
  746. 0x6b => 'ChromaticAberrationBlue',
  747. 0x6d => { #1
  748. Name => 'LuminanceNR_TIFF_JPEG',
  749. Notes => 'val = raw / 10',
  750. ValueConv => '$val / 10',
  751. ValueConvInv => 'int($val * 10 + 0.5)',
  752. },
  753. # (VRD 3.4.0 edit data ends here: 220 bytes, index 0x6e)
  754. 0x6e => { Name => 'AutoLightingOptimizerOn', %noYes },
  755. 0x6f => {
  756. Name => 'AutoLightingOptimizer',
  757. PrintConv => {
  758. 100 => 'Low',
  759. 200 => 'Standard',
  760. 300 => 'Strong',
  761. 0x7fff => 'n/a', #1
  762. },
  763. },
  764. # 0x71: 200
  765. # 0x73: 100
  766. # (VRD 3.5.0 edit data ends here: 232 bytes, index 0x74)
  767. 0x75 => {
  768. Name => 'StandardRawHighlight',
  769. ValueConv => '$val / 10',
  770. ValueConvInv => '$val * 10',
  771. },
  772. 0x76 => {
  773. Name => 'PortraitRawHighlight',
  774. ValueConv => '$val / 10',
  775. ValueConvInv => '$val * 10',
  776. },
  777. 0x77 => {
  778. Name => 'LandscapeRawHighlight',
  779. ValueConv => '$val / 10',
  780. ValueConvInv => '$val * 10',
  781. },
  782. 0x78 => {
  783. Name => 'NeutralRawHighlight',
  784. ValueConv => '$val / 10',
  785. ValueConvInv => '$val * 10',
  786. },
  787. 0x79 => {
  788. Name => 'FaithfulRawHighlight',
  789. ValueConv => '$val / 10',
  790. ValueConvInv => '$val * 10',
  791. },
  792. 0x7a => {
  793. Name => 'MonochromeRawHighlight',
  794. ValueConv => '$val / 10',
  795. ValueConvInv => '$val * 10',
  796. },
  797. 0x7b => {
  798. Name => 'UnknownRawHighlight',
  799. Unknown => 1,
  800. ValueConv => '$val / 10',
  801. ValueConvInv => '$val * 10',
  802. },
  803. 0x7c => {
  804. Name => 'CustomRawHighlight',
  805. ValueConv => '$val / 10',
  806. ValueConvInv => '$val * 10',
  807. },
  808. 0x7e => {
  809. Name => 'StandardRawShadow',
  810. ValueConv => '$val / 10',
  811. ValueConvInv => '$val * 10',
  812. },
  813. 0x7f => {
  814. Name => 'PortraitRawShadow',
  815. ValueConv => '$val / 10',
  816. ValueConvInv => '$val * 10',
  817. },
  818. 0x80 => {
  819. Name => 'LandscapeRawShadow',
  820. ValueConv => '$val / 10',
  821. ValueConvInv => '$val * 10',
  822. },
  823. 0x81 => {
  824. Name => 'NeutralRawShadow',
  825. ValueConv => '$val / 10',
  826. ValueConvInv => '$val * 10',
  827. },
  828. 0x82 => {
  829. Name => 'FaithfulRawShadow',
  830. ValueConv => '$val / 10',
  831. ValueConvInv => '$val * 10',
  832. },
  833. 0x83 => {
  834. Name => 'MonochromeRawShadow',
  835. ValueConv => '$val / 10',
  836. ValueConvInv => '$val * 10',
  837. },
  838. 0x84 => {
  839. Name => 'UnknownRawShadow',
  840. Unknown => 1,
  841. ValueConv => '$val / 10',
  842. ValueConvInv => '$val * 10',
  843. },
  844. 0x85 => {
  845. Name => 'CustomRawShadow',
  846. ValueConv => '$val / 10',
  847. ValueConvInv => '$val * 10',
  848. },
  849. 0x8b => { #2
  850. Name => 'AngleAdj',
  851. Format => 'int32s',
  852. ValueConv => '$val / 100',
  853. ValueConvInv => '$val * 100',
  854. },
  855. 0x8e => {
  856. Name => 'CheckMark2',
  857. Format => 'int16u',
  858. PrintConvColumns => 2,
  859. PrintConv => {
  860. 0 => 'Clear',
  861. 1 => 1,
  862. 2 => 2,
  863. 3 => 3,
  864. 4 => 4,
  865. 5 => 5,
  866. },
  867. },
  868. # (VRD 3.8.0 edit data ends here: 286 bytes, index 0x8f)
  869. 0x90 => {
  870. Name => 'UnsharpMask',
  871. PrintConv => { 0 => 'Off', 1 => 'On' },
  872. },
  873. 0x92 => 'StandardUnsharpMaskStrength',
  874. 0x94 => 'StandardUnsharpMaskFineness',
  875. 0x96 => 'StandardUnsharpMaskThreshold',
  876. 0x98 => 'PortraitUnsharpMaskStrength',
  877. 0x9a => 'PortraitUnsharpMaskFineness',
  878. 0x9c => 'PortraitUnsharpMaskThreshold',
  879. 0x9e => 'LandscapeUnsharpMaskStrength',
  880. 0xa0 => 'LandscapeUnsharpMaskFineness',
  881. 0xa2 => 'LandscapeUnsharpMaskThreshold',
  882. 0xa4 => 'NeutraUnsharpMaskStrength',
  883. 0xa6 => 'NeutralUnsharpMaskFineness',
  884. 0xa8 => 'NeutralUnsharpMaskThreshold',
  885. 0xaa => 'FaithfulUnsharpMaskStrength',
  886. 0xac => 'FaithfulUnsharpMaskFineness',
  887. 0xae => 'FaithfulUnsharpMaskThreshold',
  888. 0xb0 => 'MonochromeUnsharpMaskStrength',
  889. 0xb2 => 'MonochromeUnsharpMaskFineness',
  890. 0xb4 => 'MonochromeUnsharpMaskThreshold',
  891. 0xb6 => 'CustomUnsharpMaskStrength',
  892. 0xb8 => 'CustomUnsharpMaskFineness',
  893. 0xba => 'CustomUnsharpMaskThreshold',
  894. 0xbc => 'CustomDefaultUnsharpStrength',
  895. 0xbe => 'CustomDefaultUnsharpFineness',
  896. 0xc0 => 'CustomDefaultUnsharpThreshold',
  897. # (VRD 3.9.1 edit data ends here: 392 bytes, index 0xc4)
  898. # 0xc9: 3 - some RawSharpness
  899. # 0xca: 4095 - some RawHighlightPoint
  900. # 0xcb: 0 - some RawShadowPoint
  901. # 0xcc: 4095 - some OutputHighlightPoint
  902. # 0xcd: 0 - some OutputShadowPoint
  903. # 0xd1: 3 - some UnsharpMaskStrength
  904. # 0xd3: 7 - some UnsharpMaskFineness
  905. # 0xd5: 3,4 - some UnsharpMaskThreshold
  906. 0xd6 => { Name => 'CropCircleActive', %noYes },
  907. 0xd7 => 'CropCircleX',
  908. 0xd8 => 'CropCircleY',
  909. 0xd9 => 'CropCircleRadius',
  910. # 0xda: 0, 1
  911. # 0xdb: 100
  912. 0xdc => {
  913. Name => 'DLOOn',
  914. DataMember => 'DLOOn',
  915. RawConv => '$$self{DLOOn} = $val',
  916. %noYes,
  917. },
  918. 0xdd => 'DLOSetting',
  919. # (VRD 3.11.0 edit data ends here: 444 bytes, index 0xde)
  920. 0xde => {
  921. Name => 'DLOShootingDistance',
  922. Notes => '100% = infinity',
  923. RawConv => '$val == 0x7fff ? undef : $val',
  924. ValueConv => '1 - $val / 0x400',
  925. ValueConvInv => 'int((1 - $val) * 0x400 + 0.5)',
  926. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  927. PrintConvInv => 'ToFloat($val) / 100',
  928. },
  929. 0xdf => {
  930. Name => 'DLODataLength',
  931. DataMember => 'DLODataLength',
  932. Format => 'int32u',
  933. Writable => 0,
  934. RawConv => '$$self{DLODataLength} = $val',
  935. },
  936. 0xe0 => { # (yes, this overlaps DLODataLength)
  937. Name => 'DLOInfo',
  938. # - have seen DLODataLengths of 65536,64869 when DLO is Off, so must test DLOOn flag
  939. Condition => '$$self{DLOOn}',
  940. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DLOInfo' },
  941. Hook => '$varSize += $$self{DLODataLength} + 0x16',
  942. },
  943. 0xe1 => 'CameraRawColorTone',
  944. # (VRD 3.11.2 edit data ends here: 452 bytes, index 0xe2, unless DLO is on)
  945. 0xe2 => 'CameraRawSaturation',
  946. 0xe3 => 'CameraRawContrast',
  947. 0xe4 => { Name => 'CameraRawLinear', %noYes },
  948. 0xe5 => 'CameraRawSharpness',
  949. 0xe6 => 'CameraRawHighlightPoint',
  950. 0xe7 => 'CameraRawShadowPoint',
  951. 0xe8 => 'CameraRawOutputHighlightPoint',
  952. 0xe9 => 'CameraRawOutputShadowPoint',
  953. );
  954. # DLO tags (ref PH)
  955. %Image::ExifTool::CanonVRD::DLOInfo = (
  956. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  957. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  958. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  959. WRITABLE => 1,
  960. FIRST_ENTRY => 1,
  961. FORMAT => 'int16s',
  962. GROUPS => { 2 => 'Image' },
  963. NOTES => 'Tags added when DLO (Digital Lens Optimizer) is on.',
  964. # 0x01 - seen 3112,3140
  965. 0x04 => 'DLOSettingApplied',
  966. 0x05 => {
  967. Name => 'DLOVersion', #(NC)
  968. Format => 'string[10]',
  969. },
  970. 0x0a => {
  971. Name => 'DLOData',
  972. LargeTag => 1, # large tag, so avoid storing unnecessarily
  973. Notes => 'variable-length Digital Lens Optimizer data, stored in JPEG-like format',
  974. Format => 'undef[$$self{DLODataLength}]',
  975. Writable => 0,
  976. Binary => 1,
  977. },
  978. );
  979. # VRD version 4 tags (ref PH)
  980. %Image::ExifTool::CanonVRD::DR4 = (
  981. PROCESS_PROC => \&ProcessDR4,
  982. WRITE_PROC => \&ProcessDR4,
  983. WRITABLE => 1,
  984. GROUPS => { 2 => 'Image' },
  985. VARS => { HEX_ID => 1, SORT_PROC => \&SortDR4 },
  986. NOTES => q{
  987. Tags written by Canon DPP version 4 in CanonVRD trailers and DR4 files. Each
  988. tag has three associated flag words which are stored with the directory
  989. entry, some of which are extracted as a separate tag, indicated in the table
  990. below by a decimal appended to the tag ID (.0, .1 or .2).
  991. },
  992. header => {
  993. Name => 'DR4Header',
  994. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DR4Header' },
  995. },
  996. 0x10002 => 'Rotation', # left/right rotation 90,180,270
  997. 0x10003 => 'AngleAdj', # crop angle
  998. # 0x10018 - fmt=8: 0
  999. # 0x10020 - fmt=2: ''
  1000. 0x10021 => 'CustomPictureStyle', # (string)
  1001. 0x10101 => {
  1002. Name => 'CheckMark',
  1003. PrintConv => {
  1004. 0 => 'Clear',
  1005. 1 => 1,
  1006. 2 => 2,
  1007. 3 => 3,
  1008. 4 => 4,
  1009. 5 => 5,
  1010. },
  1011. },
  1012. 0x10200 => {
  1013. Name => 'WorkColorSpace',
  1014. PrintConv => {
  1015. 1 => 'sRGB',
  1016. 2 => 'Adobe RGB',
  1017. 3 => 'Wide Gamut RGB',
  1018. 4 => 'Apple RGB',
  1019. 5 => 'ColorMatch RGB',
  1020. },
  1021. },
  1022. # 0x10201 - fmt=9: 0
  1023. # 0x10f20 - fmt=9: 350
  1024. 0x20001 => 'RawBrightnessAdj',
  1025. 0x20101 => {
  1026. Name => 'WhiteBalanceAdj',
  1027. PrintConvColumns => 2,
  1028. PrintConv => {
  1029. -1 => 'Manual (Click)',
  1030. 0 => 'Auto',
  1031. 1 => 'Daylight',
  1032. 2 => 'Cloudy',
  1033. 3 => 'Tungsten',
  1034. 4 => 'Fluorescent',
  1035. 5 => 'Flash',
  1036. 8 => 'Shade',
  1037. 9 => 'Kelvin',
  1038. 255 => 'Shot Settings',
  1039. },
  1040. },
  1041. 0x20102 => 'WBAdjColorTemp',
  1042. 0x20105 => 'WBAdjMagentaGreen',
  1043. 0x20106 => 'WBAdjBlueAmber',
  1044. 0x20125 => {
  1045. Name => 'WBAdjRGGBLevels',
  1046. PrintConv => '$val =~ s/^\d+ //; $val', # remove first integer (14: what is this for?)
  1047. PrintConvInv => '"14 $val"',
  1048. },
  1049. 0x20200 => { Name => 'GammaLinear', %noYes },
  1050. 0x20301 => {
  1051. Name => 'PictureStyle',
  1052. PrintHex => 1,
  1053. PrintConv => {
  1054. 0x81 => 'Standard',
  1055. 0x82 => 'Portrait',
  1056. 0x83 => 'Landscape',
  1057. 0x84 => 'Neutral',
  1058. 0x85 => 'Faithful',
  1059. 0x86 => 'Monochrome',
  1060. 0x87 => 'Auto',
  1061. 0x88 => 'Fine Detail',
  1062. 0xf0 => 'Shot Settings',
  1063. 0xff => 'Custom',
  1064. },
  1065. },
  1066. # 0x20302 - Gamma curve data
  1067. 0x20303 => 'ContrastAdj',
  1068. 0x20304 => 'ColorToneAdj',
  1069. 0x20305 => 'ColorSaturationAdj',
  1070. 0x20306 => {
  1071. Name => 'MonochromeToningEffect',
  1072. PrintConv => {
  1073. 0 => 'None',
  1074. 1 => 'Sepia',
  1075. 2 => 'Blue',
  1076. 3 => 'Purple',
  1077. 4 => 'Green',
  1078. },
  1079. },
  1080. 0x20307 => {
  1081. Name => 'MonochromeFilterEffect',
  1082. PrintConv => {
  1083. 0 => 'None',
  1084. 1 => 'Yellow',
  1085. 2 => 'Orange',
  1086. 3 => 'Red',
  1087. 4 => 'Green',
  1088. },
  1089. },
  1090. 0x20308 => 'UnsharpMaskStrength',
  1091. 0x20309 => 'UnsharpMaskFineness',
  1092. 0x2030a => 'UnsharpMaskThreshold',
  1093. 0x2030b => 'ShadowAdj',
  1094. 0x2030c => 'HighlightAdj',
  1095. 0x20310 => {
  1096. Name => 'SharpnessAdj',
  1097. PrintConv => {
  1098. 0 => 'Sharpness',
  1099. 1 => 'Unsharp Mask',
  1100. },
  1101. },
  1102. '0x20310.0' => { Name => 'SharpnessAdjOn', %noYes },
  1103. 0x20311 => 'SharpnessStrength',
  1104. 0x20400 => {
  1105. Name => 'ToneCurve',
  1106. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::ToneCurve' },
  1107. },
  1108. '0x20400.1' => { Name => 'ToneCurveOriginal', %noYes },
  1109. # 0x20401 - fmt=33 (312 bytes)
  1110. 0x20410 => 'ToneCurveBrightness',
  1111. 0x20411 => 'ToneCurveContrast',
  1112. 0x20500 => {
  1113. Name => 'AutoLightingOptimizer',
  1114. PrintConv => {
  1115. 0 => 'Low',
  1116. 1 => 'Standard',
  1117. 2 => 'Strong',
  1118. },
  1119. },
  1120. '0x20500.0' => {
  1121. Name => 'AutoLightingOptimizerOn',
  1122. Notes => 'ignored if gamma is linear',
  1123. %noYes,
  1124. },
  1125. # 0x20501 - fmt=13: 0
  1126. # 0x20502 - fmt=13: 0
  1127. 0x20600 => 'LuminanceNoiseReduction',
  1128. 0x20601 => 'ChrominanceNoiseReduction',
  1129. # 0x20650 - fmt=9: 0 (JPG images)
  1130. 0x20701 => {
  1131. Name => 'ShootingDistance',
  1132. Notes => '100% = infinity',
  1133. ValueConv => '$val / 10',
  1134. ValueConvInv => '$val * 10',
  1135. PrintConv => 'sprintf("%.0f%%", $val * 100)',
  1136. PrintConvInv => 'ToFloat($val) / 100',
  1137. },
  1138. 0x20702 => {
  1139. Name => 'PeripheralIllumination',
  1140. PrintConv => 'sprintf "%g", $val',
  1141. PrintConvInv => '$val',
  1142. },
  1143. '0x20702.0' => { Name => 'PeripheralIlluminationOn', %noYes },
  1144. 0x20703 => {
  1145. Name => 'ChromaticAberration',
  1146. PrintConv => 'sprintf "%g", $val',
  1147. PrintConvInv => '$val',
  1148. },
  1149. '0x20703.0' => { Name => 'ChromaticAberrationOn', %noYes },
  1150. 0x20704 => { Name => 'ColorBlurOn', %noYes },
  1151. 0x20705 => {
  1152. Name => 'DistortionCorrection',
  1153. PrintConv => 'sprintf "%g", $val',
  1154. PrintConvInv => '$val',
  1155. },
  1156. '0x20705.0' => { Name => 'DistortionCorrectionOn', %noYes },
  1157. 0x20706 => 'DLOSetting',
  1158. '0x20706.0' => { Name => 'DLOOn', %noYes },
  1159. 0x20707 => {
  1160. Name => 'ChromaticAberrationRed',
  1161. PrintConv => 'sprintf "%g", $val',
  1162. PrintConvInv => '$val',
  1163. },
  1164. 0x20708 => {
  1165. Name => 'ChromaticAberrationBlue',
  1166. PrintConv => 'sprintf "%g", $val',
  1167. PrintConvInv => '$val',
  1168. },
  1169. 0x20709 => {
  1170. Name => 'DistortionEffect',
  1171. PrintConv => {
  1172. 0 => 'Shot Settings',
  1173. 1 => 'Emphasize Linearity',
  1174. 2 => 'Emphasize Distance',
  1175. 3 => 'Emphasize Periphery',
  1176. 4 => 'Emphasize Center',
  1177. },
  1178. },
  1179. # 0x20800 - fmt=1: 0
  1180. # 0x20801 - fmt=1: 0
  1181. 0x20900 => 'ColorHue',
  1182. 0x20901 => 'SaturationAdj',
  1183. 0x20910 => 'RedHSL',
  1184. 0x20911 => 'OrangeHSL',
  1185. 0x20912 => 'GreenHSL',
  1186. 0x20913 => 'AquaHSL',
  1187. 0x20914 => 'BlueHSL',
  1188. 0x20915 => 'BlueHSL',
  1189. 0x20916 => 'PurpleHSL',
  1190. 0x20917 => 'MagentaHSL',
  1191. 0x20a00 => {
  1192. Name => 'GammaInfo',
  1193. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::GammaInfo' },
  1194. },
  1195. # 0x20a01 - Auto picture style settings
  1196. # 0x20a02 - Standard picture style settings
  1197. # 0x20a03 - Portrait picture style settings
  1198. # 0x20a04 - Landscape picture style settings
  1199. # 0x20a05 - Neutral picture style settings
  1200. # 0x20a06 - Faithful picture style settings
  1201. # 0x20a07 - Monochrome picture style settings
  1202. # 0x20a08 - (unknown picture style settings)
  1203. # 0x20a09 - Custom picture style settings
  1204. # 0x20a20 - Fine Detail picture style settings
  1205. 0x30101 => {
  1206. Name => 'CropAspectRatio',
  1207. PrintConv => {
  1208. 0 => 'Free',
  1209. 1 => 'Custom',
  1210. 2 => '1:1',
  1211. 3 => '3:2',
  1212. 4 => '2:3',
  1213. 5 => '4:3',
  1214. 6 => '3:4',
  1215. 7 => '5:4',
  1216. 8 => '4:5',
  1217. 9 => '16:9',
  1218. 10 => '9:16',
  1219. },
  1220. },
  1221. 0x30102 => 'CropAspectRatioCustom',
  1222. # 0x30103 - fmt=33: "0 0 8"
  1223. 0xf0100 => {
  1224. Name => 'CropInfo',
  1225. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::CropInfo' },
  1226. },
  1227. 0xf0500 => {
  1228. Name => 'CustomPictureStyleData',
  1229. Binary => 1,
  1230. },
  1231. 0xf0510 => {
  1232. Name => 'StampInfo',
  1233. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampInfo' },
  1234. },
  1235. 0xf0511 => {
  1236. Name => 'DustInfo',
  1237. SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::DustInfo' },
  1238. },
  1239. 0xf0512 => 'LensFocalLength',
  1240. # 0xf0521 - DLO data
  1241. # 0xf0520 - DLO data
  1242. # 0xf0530 - created when dust delete data applied (4 bytes, all zero)
  1243. # 0xf0600 - fmt=253 (2308 bytes, JPG images)
  1244. # 0xf0601 - fmt=253 (2308 bytes, JPG images)
  1245. # 0x1ff52c - values: 129,130,132 (related to custom picture style somehow)
  1246. # to do:
  1247. # - find 8-15mm CR2 sample and decode linear distortion effect fine-tune
  1248. );
  1249. # Version 4 header information (ref PH)
  1250. %Image::ExifTool::CanonVRD::DR4Header = (
  1251. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1252. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  1253. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  1254. WRITABLE => 1,
  1255. FIRST_ENTRY => 0,
  1256. FORMAT => 'int32u',
  1257. GROUPS => { 2 => 'Image' },
  1258. # 0 - value: 'IIII' (presumably byte order)
  1259. # 1 - value: 0x00040004 (currently use this for magic number)
  1260. # 2 - value: 6
  1261. 3 => {
  1262. Name => 'DR4CameraModel',
  1263. Writable => 'int32u',
  1264. PrintHex => 1,
  1265. SeparateTable => 'Canon CameraModelID',
  1266. PrintConv => \%Image::ExifTool::Canon::canonModelID,
  1267. },
  1268. # 4 - value: 3
  1269. # 5 - value: 4
  1270. # 6 - value: 5
  1271. # 7 - DR4 directory entry count
  1272. );
  1273. # Version 4 RGB tone curve information (ref PH)
  1274. %Image::ExifTool::CanonVRD::ToneCurve = (
  1275. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1276. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  1277. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  1278. WRITABLE => 1,
  1279. FIRST_ENTRY => 0,
  1280. FORMAT => 'int32u',
  1281. GROUPS => { 2 => 'Image' },
  1282. 0x00 => {
  1283. Name => 'ToneCurveColorSpace',
  1284. PrintConv => {
  1285. 0 => 'RGB',
  1286. 1 => 'Luminance',
  1287. },
  1288. },
  1289. 0x01 => {
  1290. Name => 'ToneCurveShape',
  1291. PrintConv => {
  1292. 0 => 'Curve',
  1293. 1 => 'Straight',
  1294. },
  1295. },
  1296. 0x03 => { Name => 'ToneCurveInputRange', Format => 'int32u[2]', Notes => '255 max' },
  1297. 0x05 => { Name => 'ToneCurveOutputRange', Format => 'int32u[2]', Notes => '255 max' },
  1298. 0x07 => {
  1299. Name => 'RGBCurvePoints',
  1300. Format => 'int32u[21]',
  1301. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  1302. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  1303. },
  1304. 0x0a => 'ToneCurveX',
  1305. 0x0b => 'ToneCurveY',
  1306. 0x2d => {
  1307. Name => 'RedCurvePoints',
  1308. Format => 'int32u[21]',
  1309. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  1310. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  1311. },
  1312. 0x53 => {
  1313. Name => 'GreenCurvePoints',
  1314. Format => 'int32u[21]',
  1315. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  1316. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  1317. },
  1318. 0x79 => {
  1319. Name => 'BlueCurvePoints',
  1320. Format => 'int32u[21]',
  1321. PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
  1322. PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
  1323. },
  1324. );
  1325. # Version 4 gamma curve information (ref PH)
  1326. %Image::ExifTool::CanonVRD::GammaInfo = (
  1327. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1328. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  1329. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  1330. WRITABLE => 1,
  1331. FIRST_ENTRY => 0,
  1332. FORMAT => 'double',
  1333. GROUPS => { 2 => 'Image' },
  1334. 0x02 => 'GammaContrast',
  1335. 0x03 => 'GammaColorTone',
  1336. 0x04 => 'GammaSaturation',
  1337. 0x05 => 'GammaUnsharpMaskStrength',
  1338. 0x06 => 'GammaUnsharpMaskFineness',
  1339. 0x07 => 'GammaUnsharpMaskThreshold',
  1340. 0x08 => 'GammaSharpnessStrength',
  1341. 0x09 => 'GammaShadow',
  1342. 0x0a => 'GammaHighlight',
  1343. # 0x0b-0x10 are the same as first 6 doubles of tag DR4_0x20302
  1344. # 0x0b - value: 14
  1345. 0x0c => {
  1346. Name => 'GammaBlackPoint',
  1347. ValueConv => q{
  1348. return 0 if $val <= 0;
  1349. $val = log($val / 4.6875) / log(2) + 1;
  1350. return abs($val) > 1e-10 ? $val : 0;
  1351. },
  1352. ValueConvInv => '$val ? exp(($val - 1) * log(2)) * 4.6876 : 0',
  1353. PrintConv => 'sprintf("%+.3f", $val)',
  1354. PrintConvInv => '$val',
  1355. },
  1356. 0x0d => {
  1357. Name => 'GammaWhitePoint',
  1358. ValueConv => q{
  1359. return $val if $val <= 0;
  1360. $val = log($val / 4.6875) / log(2) - 11.77109325169954;
  1361. return abs($val) > 1e-10 ? $val : 0;
  1362. },
  1363. ValueConvInv => '$val ? exp(($val + 11.77109325169954) * log(2)) * 4.6875 : 0',
  1364. PrintConv => 'sprintf("%+.3f", $val)',
  1365. PrintConvInv => '$val',
  1366. },
  1367. 0x0e => {
  1368. Name => 'GammaMidPoint',
  1369. ValueConv => q{
  1370. return $val if $val <= 0;
  1371. $val = log($val / 4.6875) / log(2) - 8;
  1372. return abs($val) > 1e-10 ? $val : 0;
  1373. },
  1374. ValueConvInv => '$val ? exp(($val + 8) * log(2)) * 4.6876 : 0',
  1375. PrintConv => 'sprintf("%+.3f", $val)',
  1376. PrintConvInv => '$val',
  1377. },
  1378. 0x0f => { Name => 'GammaCurveOutputRange', Format => 'double[2]', Notes => '16383 max' },
  1379. );
  1380. # Version 4 crop information (ref PH)
  1381. %Image::ExifTool::CanonVRD::CropInfo = (
  1382. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1383. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  1384. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  1385. WRITABLE => 1,
  1386. FIRST_ENTRY => 0,
  1387. FORMAT => 'int32s',
  1388. GROUPS => { 2 => 'Image' },
  1389. 0 => { Name => 'CropActive', %noYes },
  1390. 1 => 'CropRotatedOriginalWidth',
  1391. 2 => 'CropRotatedOriginalHeight',
  1392. 3 => 'CropX',
  1393. 4 => 'CropY',
  1394. 5 => 'CropWidth',
  1395. 6 => 'CropHeight',
  1396. 8 => {
  1397. Name => 'CropRotation',
  1398. Format => 'double',
  1399. PrintConv => 'sprintf("%.7g",$val)',
  1400. PrintConvInv => '$val',
  1401. },
  1402. 0x0a => 'CropOriginalWidth',
  1403. 0x0b => 'CropOriginalHeight',
  1404. # 0x0c double - value: 100
  1405. );
  1406. # DR4 Stamp Tool tags (ref PH)
  1407. %Image::ExifTool::CanonVRD::StampInfo = (
  1408. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1409. GROUPS => { 2 => 'Image' },
  1410. FORMAT => 'int32u',
  1411. FIRST_ENTRY => 0,
  1412. 0x02 => 'StampToolCount',
  1413. );
  1414. # DR4 dust delete information (ref PH)
  1415. %Image::ExifTool::CanonVRD::DustInfo = (
  1416. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  1417. GROUPS => { 2 => 'Image' },
  1418. FORMAT => 'int32u',
  1419. FIRST_ENTRY => 0,
  1420. 0x02 => { Name => 'DustDeleteApplied', %noYes },
  1421. );
  1422. #------------------------------------------------------------------------------
  1423. # sort DR4 tag ID's for the documentation
  1424. sub SortDR4($$)
  1425. {
  1426. my ($a, $b) = @_;
  1427. my ($aHex, $aDec, $bHex, $bDec);
  1428. ($aHex, $aDec) = ($1, $2) if $a =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
  1429. ($bHex, $bDec) = ($1, $2) if $b =~ /^(0x[0-9a-f]+)?\.?(\d*?)$/;
  1430. if ($aHex) {
  1431. return 1 unless defined $bDec; # $b is 'header';
  1432. return hex($aHex) <=> hex($bHex) || $aDec <=> $bDec if $bHex;
  1433. return hex($aHex) <=> $bDec || 1;
  1434. } elsif ($bHex) {
  1435. return -1 unless defined $aDec;
  1436. return $aDec <=> hex($bHex) || -1;
  1437. } else {
  1438. return 1 unless defined $bDec;
  1439. return -1 unless defined $aDec;
  1440. return $aDec <=> $bDec;
  1441. }
  1442. }
  1443. #------------------------------------------------------------------------------
  1444. # Tone curve print conversion
  1445. sub ToneCurvePrint($)
  1446. {
  1447. my $val = shift;
  1448. my @vals = split ' ', $val;
  1449. return $val unless @vals == 21;
  1450. my $n = shift @vals;
  1451. return $val unless $n >= 2 and $n <= 10;
  1452. $val = '';
  1453. while ($n--) {
  1454. $val and $val .= ' ';
  1455. $val .= '(' . shift(@vals) . ',' . shift(@vals) . ')';
  1456. }
  1457. return $val;
  1458. }
  1459. #------------------------------------------------------------------------------
  1460. # Inverse print conversion for tone curve
  1461. sub ToneCurvePrintInv($)
  1462. {
  1463. my $val = shift;
  1464. my @vals = ($val =~ /\((\d+),(\d+)\)/g);
  1465. return undef unless @vals >= 4 and @vals <= 20 and not @vals & 0x01;
  1466. unshift @vals, scalar(@vals) / 2;
  1467. while (@vals < 21) { push @vals, 0 }
  1468. return join(' ',@vals);
  1469. }
  1470. #------------------------------------------------------------------------------
  1471. # Read/Write VRD edit data
  1472. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  1473. # Returns: Reading: 1 on success; Writing: modified edit data, or undef if nothing changed
  1474. sub ProcessEditData($$$)
  1475. {
  1476. my ($et, $dirInfo, $tagTablePtr) = @_;
  1477. $et or return 1; # allow dummy access
  1478. my $dataPt = $$dirInfo{DataPt};
  1479. my $pos = $$dirInfo{DirStart};
  1480. my $dataPos = $$dirInfo{DataPos};
  1481. my $outfile = $$dirInfo{OutFile};
  1482. my $dirLen = $$dirInfo{DirLen};
  1483. my $verbose = $et->Options('Verbose');
  1484. my $out = $et->Options('TextOut');
  1485. my $oldChanged = $$et{CHANGED};
  1486. $et->VerboseDir('VRD Edit Data', 0, $dirLen) unless $outfile;
  1487. if ($outfile) {
  1488. # make a copy for editing in place
  1489. my $buff = substr($$dataPt, $pos, $dirLen);
  1490. $dataPt = $$dirInfo{DataPt} = \$buff;
  1491. $pos = $$dirInfo{DirStart} = 0;
  1492. }
  1493. my $dirEnd = $pos + $dirLen;
  1494. # loop through all records in the edit data
  1495. my ($recNum, $recLen, $err);
  1496. for ($recNum=0;; ++$recNum, $pos+=$recLen) {
  1497. if ($pos + 4 > $dirEnd) {
  1498. last if $pos == $dirEnd; # all done if we arrived at end
  1499. $recLen = 0; # just reset record size (will exit loop on test below)
  1500. } else {
  1501. $recLen = Get32u($dataPt, $pos);
  1502. # (DR4 has a null terminator)
  1503. last if $recLen == 0 and $pos + 4 == $dirEnd;
  1504. }
  1505. $pos += 4; # move to start of record
  1506. if ($pos + $recLen > $dirEnd) {
  1507. $et->Warn('Possibly corrupt CanonVRD Edit record');
  1508. $err = 1;
  1509. last;
  1510. }
  1511. my $saveRecLen = $recLen;
  1512. if ($verbose > 1 and not $outfile) {
  1513. printf $out "$$et{INDENT}CanonVRD Edit record ($recLen bytes at offset 0x%x)\n",
  1514. $pos + $dataPos;
  1515. if ($recNum and $verbose > 2) {
  1516. my %parms = (
  1517. Start => $pos,
  1518. Addr => $pos + $dataPos,
  1519. Out => $out,
  1520. Prefix => $$et{INDENT},
  1521. );
  1522. $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
  1523. HexDump($dataPt, $recLen, %parms);
  1524. }
  1525. }
  1526. # our edit information is the 0th record, so don't process the others
  1527. next if $recNum;
  1528. # process VRD edit information
  1529. my $subTablePtr = $tagTablePtr;
  1530. my $index;
  1531. my %subdirInfo = (
  1532. DataPt => $dataPt,
  1533. DataPos => $dataPos,
  1534. DirStart => $pos,
  1535. DirLen => $recLen,
  1536. OutFile => $outfile,
  1537. );
  1538. my $subStart = 0;
  1539. # loop through various sections of the VRD edit data
  1540. for ($index=0; ; ++$index) {
  1541. my $tagInfo = $$subTablePtr{$index} or last;
  1542. my $subLen;
  1543. my $maxLen = $recLen - $subStart;
  1544. if ($$tagInfo{Size}) {
  1545. $subLen = $$tagInfo{Size};
  1546. } elsif (defined $$tagInfo{Size}) {
  1547. # get size from int32u at $subStart
  1548. last unless $subStart + 4 <= $recLen;
  1549. $subLen = Get32u($dataPt, $subStart + $pos);
  1550. $subStart += 4; # skip the length word
  1551. } else {
  1552. $subLen = $maxLen;
  1553. }
  1554. $subLen > $maxLen and $subLen = $maxLen;
  1555. if ($subLen) {
  1556. my $subTable = GetTagTable($$tagInfo{SubDirectory}{TagTable});
  1557. my $subName = $$tagInfo{Name};
  1558. $subdirInfo{DirStart} = $subStart + $pos;
  1559. $subdirInfo{DirLen} = $subLen;
  1560. $subdirInfo{DirName} = $subName;
  1561. if ($outfile) {
  1562. # rewrite this section of the VRD edit information
  1563. $verbose and print $out " Rewriting Canon $subName\n";
  1564. my $newVal = $et->WriteDirectory(\%subdirInfo, $subTable);
  1565. if ($newVal) {
  1566. my $sizeDiff = length($newVal) - $subLen;
  1567. substr($$dataPt, $pos+$subStart, $subLen) = $newVal;
  1568. if ($sizeDiff) {
  1569. $subLen = length $newVal;
  1570. $recLen += $sizeDiff;
  1571. $dirEnd += $sizeDiff;
  1572. $dirLen += $sizeDiff;
  1573. }
  1574. }
  1575. } else {
  1576. $et->VPrint(0, "$$et{INDENT}$subName (SubDirectory) -->\n");
  1577. $et->VerboseDump($dataPt,
  1578. Start => $pos + $subStart,
  1579. Addr => $dataPos + $pos + $subStart,
  1580. Len => $subLen,
  1581. );
  1582. # extract tags from this section of the VRD edit information
  1583. $et->ProcessDirectory(\%subdirInfo, $subTable);
  1584. }
  1585. }
  1586. # next section starts at the end of this one
  1587. $subStart += $subLen;
  1588. }
  1589. if ($outfile and $saveRecLen ne $recLen) {
  1590. # update record length if necessary
  1591. Set32u($recLen, $dataPt, $pos - 4)
  1592. }
  1593. }
  1594. if ($outfile) {
  1595. return undef if $oldChanged == $$et{CHANGED};
  1596. return substr($$dataPt, $$dirInfo{DirStart}, $dirLen);
  1597. }
  1598. return $err ? 0 : 1;
  1599. }
  1600. #------------------------------------------------------------------------------
  1601. # Process VRD IHL data
  1602. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  1603. # Returns: 1 on success
  1604. sub ProcessIHL($$$)
  1605. {
  1606. my ($et, $dirInfo, $tagTablePtr) = @_;
  1607. my $dataPt = $$dirInfo{DataPt};
  1608. my $dataPos = $$dirInfo{DataPos};
  1609. my $pos = $$dirInfo{DirStart};
  1610. my $dirLen = $$dirInfo{DirLen};
  1611. my $dirEnd = $pos + $dirLen;
  1612. $et->VerboseDir('VRD IHL', 0, $dirLen);
  1613. SetByteOrder('II'); # (make up your mind, Canon!)
  1614. while ($pos + 48 <= $dirEnd) {
  1615. my $hdr = substr($$dataPt, $pos, 48);
  1616. unless ($hdr =~ /^IHL Created Optional Item Data\0\0/) {
  1617. $et->Warn('Possibly corrupted VRD IHL data');
  1618. last;
  1619. }
  1620. my $tag = Get32u($dataPt, $pos + 36);
  1621. my $size = Get32u($dataPt, $pos + 40); # size of data in IHL record
  1622. my $next = Get32u($dataPt, $pos + 44); # size of complete IHL record
  1623. if ($size > $next or $pos + 48 + $next > $dirEnd) {
  1624. $et->Warn(sprintf('Bad size for VRD IHL tag 0x%.4x', $tag));
  1625. last;
  1626. }
  1627. $pos += 48;
  1628. $et->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size),
  1629. DataPt => $dataPt,
  1630. DataPos => $dataPos,
  1631. Start => $pos,
  1632. Size => $size
  1633. );
  1634. $pos += $next;
  1635. }
  1636. return 1;
  1637. }
  1638. #------------------------------------------------------------------------------
  1639. # Process VRD IHL EXIF data
  1640. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  1641. # Returns: 1 on success
  1642. sub ProcessIHLExif($$$)
  1643. {
  1644. my ($et, $dirInfo, $tagTablePtr) = @_;
  1645. $$et{DOC_NUM} = 1;
  1646. # the IHL-edited maker notes may look messed up, but the offsets should be OK
  1647. my $oldFix = $et->Options(FixBase => 0);
  1648. my $rtnVal = $et->ProcessTIFF($dirInfo, $tagTablePtr);
  1649. $et->Options(FixBase => $oldFix);
  1650. delete $$et{DOC_NUM};
  1651. return $rtnVal;
  1652. }
  1653. #------------------------------------------------------------------------------
  1654. # Wrap DR4 data with the VRD header/footer and edit record
  1655. # Inputs: 0) DR4 record
  1656. # Returns: VRD[Edit[DR4]] data
  1657. sub WrapDR4($)
  1658. {
  1659. my $val = shift;
  1660. my $n = length $val;
  1661. my $oldOrder = GetByteOrder();
  1662. SetByteOrder('MM');
  1663. $val = $blankHeader . "\xff\xff\0\xf7" . Set32u($n+8) . Set32u($n) .
  1664. $val . "\0\0\0\0" . $blankFooter;
  1665. # update the new VRD length in the header/footer
  1666. Set32u($n + 16, \$val, 0x18); # (extra 16 bytes for the edit record wrapper)
  1667. Set32u($n + 16, \$val, length($val) - 0x2c);
  1668. SetByteOrder($oldOrder);
  1669. return $val;
  1670. }
  1671. #------------------------------------------------------------------------------
  1672. # Read/Write DPP version 4 edit data or DR4 file
  1673. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  1674. # Returns:
  1675. # Reading from memory (not RAF and not IsWriting): 1 on success
  1676. # Editing from memory (not RAF and IsWriting): modified edit data, or undef if nothing changed
  1677. # Reading file (RAF and not OutFile): 1 if a valid DR4 file, 0 if not
  1678. # Writing file (RAF and OutFile): 1 if valid DR4 file, 0 if not, -1 on write error
  1679. # (serves me right for not having a consistent interface for the various modes of operation)
  1680. sub ProcessDR4($$;$)
  1681. {
  1682. my ($et, $dirInfo, $tagTablePtr) = @_;
  1683. $et or return 1; # allow dummy access
  1684. my $dataPt = $$dirInfo{DataPt};
  1685. my $raf = $$dirInfo{RAF};
  1686. my $outfile = $$dirInfo{OutFile};
  1687. my $isWriting = $outfile || $$dirInfo{IsWriting};
  1688. my $dataPos = $$dirInfo{DataPos} || 0;
  1689. my $verbose = $et->Options('Verbose');
  1690. my $unknown = $et->Options('Unknown');
  1691. my ($pos, $dirLen, $numEntries, $err, $newTags);
  1692. # write CanonDR4 as a block if specified
  1693. if ($isWriting) {
  1694. my $nvHash;
  1695. my $newVal = $et->GetNewValue('CanonDR4', \$nvHash);
  1696. if ($newVal) {
  1697. $et->VPrint(0, " Writing CanonDR4 as a block\n");
  1698. $$et{DidCanonVRD} = 1; # set flag so we don't add this twice
  1699. ++$$et{CHANGED};
  1700. if ($outfile) {
  1701. Write($$dirInfo{OutFile}, $newVal) or return -1;
  1702. return 1;
  1703. } else {
  1704. return $newVal;
  1705. }
  1706. } elsif (not $dataPt and ($nvHash or $$et{DEL_GROUP}{CanonVRD})) {
  1707. $et->Error("Can't delete all CanonDR4 information from a DR4 file");
  1708. return 1;
  1709. }
  1710. }
  1711. if ($dataPt) {
  1712. $pos = $$dirInfo{DirStart} || 0;
  1713. $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $pos;
  1714. } else {
  1715. # load DR4 file into memory
  1716. my $buff;
  1717. $raf->Read($buff, 8) == 8 and $buff eq "IIII\x04\0\x04\0" or return 0;
  1718. $et->SetFileType();
  1719. $raf->Seek(0, 2) or return $err = 1;
  1720. $dirLen = $raf->Tell();
  1721. $raf->Seek(0, 0) or return $err = 1;
  1722. $raf->Read($buff, $dirLen) == $dirLen or $err = 1;
  1723. $err and $et->Warn('Error reading DR4 file'), return 1;
  1724. $tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::DR4');
  1725. $dataPt = \$buff;
  1726. $pos = 0;
  1727. }
  1728. my $dirEnd = $pos + $dirLen;
  1729. if (($$et{TAGS_FROM_FILE} and
  1730. not $$et{EXCL_TAG_LOOKUP}{canondr4}) or $$et{REQ_TAG_LOOKUP}{canondr4})
  1731. {
  1732. # extract CanonDR4 block if copying tags, or if requested
  1733. $et->FoundTag('CanonDR4', substr($$dataPt, $pos, $dirLen));
  1734. }
  1735. # version 4 header is 32 bytes (int32u[8])
  1736. if ($dirLen < 32) {
  1737. $err = 1;
  1738. } else {
  1739. SetByteOrder(substr($$dataPt, $pos, 2)) or $err = 1;
  1740. # process the DR4 header
  1741. my %hdrInfo = (
  1742. DataPt => $dataPt,
  1743. DirStart => $pos,
  1744. DirLen => 32,
  1745. DirName => 'DR4Header',
  1746. );
  1747. my $hdrTable = GetTagTable('Image::ExifTool::CanonVRD::DR4Header');
  1748. if ($outfile) {
  1749. my $hdr = $et->WriteDirectory(\%hdrInfo, $hdrTable);
  1750. substr($$dataPt, $pos, 32) = $hdr if $hdr and length $hdr == 32;
  1751. } else {
  1752. $et->VerboseDir('DR4Header', undef, 32);
  1753. $et->ProcessDirectory(\%hdrInfo, $hdrTable);
  1754. }
  1755. # number of entries in the DR4 directory
  1756. $numEntries = Get32u($dataPt, $pos + 28);
  1757. $err = 1 if $dirLen < 36 + 28 * $numEntries;
  1758. }
  1759. $err and $et->Warn('Invalid DR4 directory'), return $outfile ? undef : 0;
  1760. if ($outfile) {
  1761. $newTags = $et->GetNewTagInfoHash($tagTablePtr);
  1762. } else {
  1763. $et->VerboseDir('DR4', $numEntries, $dirLen);
  1764. }
  1765. my $index;
  1766. for ($index=0; $index<$numEntries; ++$index) {
  1767. my ($val, @flg, $i);
  1768. my $entry = $pos + 36 + 28 * $index;
  1769. last if $entry + 28 > $dirEnd;
  1770. my $tag = Get32u($dataPt, $entry);
  1771. my $fmt = Get32u($dataPt, $entry + 4);
  1772. $flg[0] = Get32u($dataPt, $entry + 8);
  1773. $flg[1] = Get32u($dataPt, $entry + 12);
  1774. $flg[2] = Get32u($dataPt, $entry + 16);
  1775. my $off = Get32u($dataPt, $entry + 20) + $pos;
  1776. my $len = Get32u($dataPt, $entry + 24);
  1777. next if $off + $len >= $dirEnd;
  1778. my $format = $vrdFormat{$fmt};
  1779. if (not $format) {
  1780. $val = unpack 'H*', substr($$dataPt, $off, $len);
  1781. $format = 'undef';
  1782. } elsif ($format eq 'double' and $len eq 8) {
  1783. # avoid teeny weeny values
  1784. $val = ReadValue($dataPt, $off, $format, undef, $len);
  1785. $val = 0 if abs($val) < 1e-100;
  1786. }
  1787. if ($outfile) {
  1788. # write (binary data) subdirectory if it exists
  1789. my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
  1790. if ($tagInfo and $$tagInfo{SubDirectory}) {
  1791. my %subdirInfo = (
  1792. DataPt => $dataPt,
  1793. DirStart => $off,
  1794. DirLen => $len,
  1795. DirName => $$tagInfo{Name},
  1796. );
  1797. my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
  1798. my $saveChanged = $$et{CHANGED};
  1799. my $dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
  1800. if (defined $dat and length($dat) == $len) {
  1801. substr($$dataPt, $off, $len) = $dat;
  1802. } else {
  1803. $$et{CHANGED} = $saveChanged; # didn't change anything after all
  1804. }
  1805. } else {
  1806. # loop through main tag and flags (don't yet consider flag 2)
  1807. for ($i=-1; $i<2; ++$i) {
  1808. $tagInfo = $$newTags{$i>=0 ? sprintf('0x%x.%d',$tag,$i) : $tag};
  1809. next unless $tagInfo;
  1810. if ($i >= 0) {
  1811. $off = $entry + 8 + 4 * $i;
  1812. $format = 'int32u';
  1813. $len = 4;
  1814. undef $val;
  1815. }
  1816. $val = ReadValue($dataPt, $off, $format, undef, $len) unless defined $val;
  1817. my $nvHash;
  1818. my $newVal = $et->GetNewValue($tagInfo, \$nvHash);
  1819. if ($et->IsOverwriting($nvHash, $val) and defined $newVal) {
  1820. my $count = int($len / Image::ExifTool::FormatSize($format));
  1821. my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $off);
  1822. if (defined $rtnVal) {
  1823. $et->VerboseValue("- CanonVRD:$$tagInfo{Name}", $val);
  1824. $et->VerboseValue("+ CanonVRD:$$tagInfo{Name}", $newVal);
  1825. ++$$et{CHANGED};
  1826. }
  1827. }
  1828. }
  1829. }
  1830. next;
  1831. }
  1832. $et->HandleTag($tagTablePtr, $tag, $val,
  1833. DataPt => $dataPt,
  1834. DataPos => $dataPos,
  1835. Start => $off,
  1836. Size => $len,
  1837. Index => $index,
  1838. Format => $format,
  1839. # $flg[0] is on/off flag
  1840. # $flg[1] "is default" flag?
  1841. # $flg[2] changed to 0 when some unsharp mask settings were changed
  1842. Extra => ", fmt=$fmt flags=" . join(',', @flg),
  1843. );
  1844. foreach $i (0..2) {
  1845. my $flagID = sprintf('0x%x.%d', $tag, $i);
  1846. $et->HandleTag($tagTablePtr, $flagID, $flg[$i]) if $$tagTablePtr{$flagID};
  1847. }
  1848. }
  1849. return 1 unless $isWriting;
  1850. return substr($$dataPt, $pos, $dirLen) unless $raf;
  1851. return 1 if Write($outfile, substr($$dataPt, $pos, $dirLen));
  1852. return -1;
  1853. }
  1854. #------------------------------------------------------------------------------
  1855. # Read/write Canon VRD file
  1856. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  1857. # Returns: 1 if this was a Canon VRD file, 0 otherwise, -1 on write error
  1858. sub ProcessVRD($$)
  1859. {
  1860. my ($et, $dirInfo) = @_;
  1861. my $raf = $$dirInfo{RAF};
  1862. my $buff;
  1863. my $num = $raf->Read($buff, 0x1c);
  1864. # initialize write directories if necessary
  1865. $et->InitWriteDirs(\%vrdMap, 'XMP') if $$dirInfo{OutFile};
  1866. if (not $num and $$dirInfo{OutFile}) {
  1867. # create new VRD file from scratch
  1868. my $newVal = $et->GetNewValue('CanonVRD');
  1869. if ($newVal) {
  1870. $et->VPrint(0, " Writing CanonVRD as a block\n");
  1871. Write($$dirInfo{OutFile}, $newVal) or return -1;
  1872. $$et{DidCanonVRD} = 1;
  1873. ++$$et{CHANGED};
  1874. } else {
  1875. # allow VRD to be created from individual tags
  1876. if ($$et{ADD_DIRS}{CanonVRD}) {
  1877. my $newVal = '';
  1878. if (ProcessCanonVRD($et, { OutFile => \$newVal }) > 0) {
  1879. Write($$dirInfo{OutFile}, $newVal) or return -1;
  1880. ++$$et{CHANGED};
  1881. return 1;
  1882. }
  1883. }
  1884. $et->Error('No CanonVRD information to write');
  1885. }
  1886. } else {
  1887. $num == 0x1c or return 0;
  1888. $buff =~ /^CANON OPTIONAL DATA\0/ or return 0;
  1889. $et->SetFileType();
  1890. $$dirInfo{DirName} = 'CanonVRD'; # set directory name for verbose output
  1891. my $result = ProcessCanonVRD($et, $dirInfo);
  1892. return $result if $result < 0;
  1893. $result or $et->Warn('Format error in VRD file');
  1894. }
  1895. return 1;
  1896. }
  1897. #------------------------------------------------------------------------------
  1898. # Write VRD data record as a block
  1899. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  1900. # Returns: VRD data block (may be empty if no VRD data)
  1901. # Notes: Increments ExifTool CHANGED flag if changed
  1902. sub WriteCanonVRD($$;$)
  1903. {
  1904. my ($et, $dirInfo, $tagTablePtr) = @_;
  1905. $et or return 1; # allow dummy access
  1906. my $nvHash = $et->GetNewValueHash($Image::ExifTool::Extra{CanonVRD});
  1907. my $val = $et->GetNewValue($nvHash);
  1908. $val = '' unless defined $val;
  1909. return undef unless $et->IsOverwriting($nvHash, $val);
  1910. ++$$et{CHANGED};
  1911. return $val;
  1912. }
  1913. #------------------------------------------------------------------------------
  1914. # Read/write CanonVRD information (from VRD file or VRD trailer)
  1915. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  1916. # Returns: 1 on success, 0 not valid VRD, or -1 error writing
  1917. # - updates DataPos to point to start of CanonVRD information
  1918. # - updates DirLen to existing trailer length
  1919. sub ProcessCanonVRD($$;$)
  1920. {
  1921. my ($et, $dirInfo, $tagTablePtr) = @_;
  1922. my $raf = $$dirInfo{RAF};
  1923. my $offset = $$dirInfo{Offset} || 0;
  1924. my $outfile = $$dirInfo{OutFile};
  1925. my $dataPt = $$dirInfo{DataPt};
  1926. my $verbose = $et->Options('Verbose');
  1927. my $out = $et->Options('TextOut');
  1928. my ($buff, $created, $err, $blockLen, $blockType, %didDir, $fromFile);
  1929. #
  1930. # The CanonVRD trailer has a 0x1c-byte header and a 0x40-byte footer,
  1931. # each beginning with "CANON OPTIONAL DATA\0" and containing an int32u
  1932. # giving the size of the contained data (at byte 0x18 and 0x14 respectively)
  1933. #
  1934. if ($raf) {
  1935. $fromFile = 1;
  1936. } else {
  1937. unless ($dataPt) {
  1938. return 1 unless $outfile;
  1939. # create blank VRD data from scratch
  1940. my $blank = $blankHeader . $blankFooter;
  1941. $dataPt = \$blank;
  1942. $verbose and print $out " Creating CanonVRD trailer\n";
  1943. $created = 1;
  1944. }
  1945. $raf = new File::RandomAccess($dataPt);
  1946. }
  1947. # read and validate the footer
  1948. $raf->Seek(-0x40-$offset, 2) or return 0;
  1949. $raf->Read($buff, 0x40) == 0x40 or return 0;
  1950. $buff =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
  1951. my $dirLen = unpack('N', $1) + 0x5c; # size including header+footer
  1952. # read and validate the header
  1953. unless ($dirLen < 0x80000000 and
  1954. $raf->Seek(-$dirLen, 1) and
  1955. $raf->Read($buff, 0x1c) == 0x1c and
  1956. $buff =~ /^CANON OPTIONAL DATA\0/ and
  1957. $raf->Seek(-0x1c, 1))
  1958. {
  1959. $et->Warn('Bad CanonVRD trailer');
  1960. return 0;
  1961. }
  1962. # set variables returned in dirInfo hash
  1963. $$dirInfo{DataPos} = $raf->Tell();
  1964. $$dirInfo{DirLen} = $dirLen;
  1965. if ($outfile and ref $outfile eq 'SCALAR' and not length $$outfile) {
  1966. # write directly to outfile to avoid duplicating data in memory
  1967. $$outfile = $$dataPt unless $fromFile;
  1968. # TRICKY! -- copy to outfile memory buffer and edit in place
  1969. # (so we must disable all Write() calls for this case)
  1970. $dataPt = $outfile;
  1971. }
  1972. if ($fromFile) {
  1973. $dataPt = \$buff unless $dataPt;
  1974. # read VRD data into memory if necessary
  1975. unless ($raf->Read($$dataPt, $dirLen) == $dirLen) {
  1976. $$dataPt = '' if $outfile and $outfile eq $dataPt;
  1977. $et->Warn('Error reading CanonVRD data');
  1978. return 0;
  1979. }
  1980. }
  1981. # exit quickly if writing and no CanonVRD tags are being edited
  1982. if ($outfile and not exists $$et{EDIT_DIRS}{CanonVRD}) {
  1983. print $out "$$et{INDENT} [nothing changed]\n" if $verbose;
  1984. return 1 if $outfile eq $dataPt;
  1985. return Write($outfile, $$dataPt) ? 1 : -1;
  1986. }
  1987. my $vrdType = 'VRD';
  1988. if ($outfile) {
  1989. $verbose and not $created and print $out " Rewriting CanonVRD trailer\n";
  1990. # delete CanonVRD information if specified
  1991. my $doDel = $$et{DEL_GROUP}{CanonVRD};
  1992. unless ($doDel) {
  1993. $doDel = 1 if $$et{DEL_GROUP}{Trailer} and $$et{FILE_TYPE} ne 'VRD';
  1994. unless ($doDel) {
  1995. # also delete if writing as a block (will get added back again later)
  1996. if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonVRD}}) {
  1997. # delete if this isn't version 4
  1998. $doDel = 1 unless $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
  1999. }
  2000. if ($$et{NEW_VALUE}{$Image::ExifTool::Extra{CanonDR4}} and not $doDel) {
  2001. # delete if this is version 4
  2002. $doDel = 1 if $$dataPt =~ /^.{28}\xff\xff\0\xf7/s;
  2003. }
  2004. }
  2005. }
  2006. if ($doDel) {
  2007. if ($$et{FILE_TYPE} eq 'VRD') {
  2008. my $newVal = $et->GetNewValue('CanonVRD');
  2009. if ($newVal) {
  2010. $verbose and print $out " Writing CanonVRD as a block\n";
  2011. if ($outfile eq $dataPt) {
  2012. $$outfile = $newVal;
  2013. } else {
  2014. Write($outfile, $newVal) or return -1;
  2015. }
  2016. $$et{DidCanonVRD} = 1;
  2017. ++$$et{CHANGED};
  2018. } else {
  2019. $et->Error("Can't delete all CanonVRD information from a VRD file");
  2020. }
  2021. } else {
  2022. $verbose and print $out " Deleting CanonVRD trailer\n";
  2023. $$outfile = '' if $outfile eq $dataPt;
  2024. ++$$et{CHANGED};
  2025. }
  2026. return 1;
  2027. }
  2028. # write now and return if CanonVRD was set as a block
  2029. my $val = $et->GetNewValue('CanonVRD');
  2030. unless ($val) {
  2031. $val = $et->GetNewValue('CanonDR4');
  2032. $vrdType = 'DR4' if $val;
  2033. }
  2034. if ($val) {
  2035. $verbose and print $out " Writing Canon$vrdType as a block\n";
  2036. # must wrap DR4 data with the VRD header/footer and edit record
  2037. $val = WrapDR4($val) if $vrdType eq 'DR4';
  2038. if ($outfile eq $dataPt) {
  2039. $$outfile = $val;
  2040. } else {
  2041. Write($outfile, $val) or return -1;
  2042. }
  2043. $$et{DidCanonVRD} = 1;
  2044. ++$$et{CHANGED};
  2045. return 1;
  2046. }
  2047. } elsif ($verbose or $$et{HTML_DUMP}) {
  2048. $et->DumpTrailer($dirInfo) if $$dirInfo{RAF};
  2049. }
  2050. $tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::Main');
  2051. # validate VRD trailer and get position and length of edit record
  2052. SetByteOrder('MM'); # VRD header/footer is big-endian
  2053. my $pos = 0x1c; # start at end of header
  2054. # loop through the VRD blocks
  2055. for (;;) {
  2056. my $end = $dirLen - 0x40; # end of last VRD block (and start of footer)
  2057. if ($pos + 8 > $end) {
  2058. last if $pos == $end;
  2059. $blockLen = $end; # mark as invalid
  2060. } else {
  2061. $blockType = Get32u($dataPt, $pos);
  2062. $blockLen = Get32u($dataPt, $pos + 4);
  2063. }
  2064. $vrdType = 'DR4' if $blockType eq 0xffff00f7;
  2065. $pos += 8; # move to start of block
  2066. if ($pos + $blockLen > $end) {
  2067. $et->Warn('Possibly corrupt CanonVRD block');
  2068. last;
  2069. }
  2070. if ($verbose > 1 and not $outfile) {
  2071. printf $out " CanonVRD block 0x%.8x ($blockLen bytes at offset 0x%x)\n",
  2072. $blockType, $pos + $$dirInfo{DataPos};
  2073. if ($verbose > 2) {
  2074. my %parms = (
  2075. Start => $pos,
  2076. Addr => $pos + $$dirInfo{DataPos},
  2077. Out => $out,
  2078. Prefix => $$et{INDENT},
  2079. );
  2080. $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5;
  2081. HexDump($dataPt, $blockLen, %parms);
  2082. }
  2083. }
  2084. my $tagInfo = $$tagTablePtr{$blockType};
  2085. unless ($tagInfo) {
  2086. unless ($et->Options('Unknown')) {
  2087. $pos += $blockLen; # step to next block
  2088. next;
  2089. }
  2090. my $name = sprintf('CanonVRD_0x%.8x', $blockType);
  2091. my $desc = $name;
  2092. $desc =~ tr/_/ /;
  2093. $tagInfo = {
  2094. Name => $name,
  2095. Description => $desc,
  2096. Binary => 1,
  2097. };
  2098. AddTagToTable($tagTablePtr, $blockType, $tagInfo);
  2099. }
  2100. if ($$tagInfo{SubDirectory}) {
  2101. my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable});
  2102. my %subdirInfo = (
  2103. DataPt => $dataPt,
  2104. DataLen => length $$dataPt,
  2105. DataPos => $$dirInfo{DataPos},
  2106. DirStart => $pos,
  2107. DirLen => $blockLen,
  2108. DirName => $$tagInfo{Name},
  2109. Parent => 'CanonVRD',
  2110. OutFile => $outfile,
  2111. );
  2112. if ($outfile) {
  2113. # set flag indicating we did this directory
  2114. $didDir{$$tagInfo{Name}} = 1;
  2115. my ($dat, $diff);
  2116. if ($$et{NEW_VALUE}{$tagInfo}) {
  2117. # write as a block
  2118. $et->VPrint(0, "Writing $$tagInfo{Name} as a block\n");
  2119. $dat = $et->GetNewValue($tagInfo);
  2120. $dat = '' unless defined $dat;
  2121. ++$$et{CHANGED};
  2122. } else {
  2123. $dat = $et->WriteDirectory(\%subdirInfo, $subTablePtr);
  2124. }
  2125. # update data with new directory
  2126. if (defined $dat) {
  2127. if (length $dat or $$et{FILE_TYPE} !~ /^(CRW|VRD)$/) {
  2128. # replace with new block (updating the block length word)
  2129. substr($$dataPt, $pos-4, $blockLen+4) = Set32u(length $dat) . $dat;
  2130. } else {
  2131. # remove block totally (CRW/VRD files only)
  2132. substr($$dataPt, $pos-8, $blockLen+8) = '';
  2133. }
  2134. # make necessary adjustments if block changes length
  2135. if (($diff = length($$dataPt) - $dirLen) != 0) {
  2136. $pos += $diff;
  2137. $dirLen += $diff;
  2138. # update the new VRD length in the header/footer
  2139. Set32u($dirLen - 0x5c, $dataPt, 0x18);
  2140. Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
  2141. }
  2142. }
  2143. } else {
  2144. # extract as a block if requested
  2145. $et->ProcessDirectory(\%subdirInfo, $subTablePtr);
  2146. }
  2147. } else {
  2148. $et->HandleTag($tagTablePtr, $blockType, substr($$dataPt, $pos, $blockLen));
  2149. }
  2150. $pos += $blockLen; # step to next block
  2151. }
  2152. if ($outfile) {
  2153. # create XMP block if necessary (CRW/VRD files only)
  2154. if ($$et{ADD_DIRS}{CanonVRD} and not $didDir{XMP}) {
  2155. my $subTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
  2156. my $dat = $et->WriteDirectory({ Parent => 'CanonVRD' }, $subTablePtr);
  2157. if ($dat) {
  2158. my $blockLen = length $dat;
  2159. substr($$dataPt, -0x40, 0) = Set32u(0xffff00f6) . Set32u(length $dat) . $dat;
  2160. $dirLen = length $$dataPt;
  2161. # update the new VRD length in the header/footer
  2162. Set32u($dirLen - 0x5c, $dataPt, 0x18);
  2163. Set32u($dirLen - 0x5c, $dataPt, $dirLen - 0x2c);
  2164. }
  2165. }
  2166. # write CanonVRD trailer unless it is empty
  2167. if (length $$dataPt) {
  2168. Write($outfile, $$dataPt) or $err = 1 unless $outfile eq $dataPt;
  2169. } else {
  2170. $verbose and print $out " Deleting CanonVRD trailer\n";
  2171. }
  2172. } elsif ($vrdType eq 'VRD' and (($$et{TAGS_FROM_FILE} and
  2173. not $$et{EXCL_TAG_LOOKUP}{canonvrd}) or $$et{REQ_TAG_LOOKUP}{canonvrd}))
  2174. {
  2175. # extract CanonVRD block if copying tags, or if requested (and not DR4 info)
  2176. $et->FoundTag('CanonVRD', $buff);
  2177. }
  2178. undef $buff;
  2179. return $err ? -1 : 1;
  2180. }
  2181. 1; # end
  2182. __END__
  2183. =head1 NAME
  2184. Image::ExifTool::CanonVRD - Read/write Canon VRD and DR4 information
  2185. =head1 SYNOPSIS
  2186. This module is used by Image::ExifTool
  2187. =head1 DESCRIPTION
  2188. This module contains definitions required by Image::ExifTool to read and
  2189. write VRD and DR4 Recipe Data information as written by the Canon Digital
  2190. Photo Professional software. This information is written to VRD and DR4
  2191. files, and as a trailer in JPEG, CRW, CR2 and TIFF images.
  2192. =head1 AUTHOR
  2193. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  2194. This library is free software; you can redistribute it and/or modify it
  2195. under the same terms as Perl itself.
  2196. =head1 ACKNOWLEDGEMENTS
  2197. Thanks to Bogdan and Gert Kello for decoding some tags.
  2198. =head1 SEE ALSO
  2199. L<Image::ExifTool::TagNames/CanonVRD Tags>,
  2200. L<Image::ExifTool(3pm)|Image::ExifTool>
  2201. =cut