PanasonicRaw.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. #------------------------------------------------------------------------------
  2. # File: PanasonicRaw.pm
  3. #
  4. # Description: Read/write Panasonic/Leica RAW/RW2/RWL meta information
  5. #
  6. # Revisions: 2009/03/24 - P. Harvey Created
  7. # 2009/05/12 - PH Added RWL file type (same format as RW2)
  8. #
  9. # References: 1) CPAN forum post by 'hardloaf' (http://www.cpanforum.com/threads/2183)
  10. # 2) http://www.cybercom.net/~dcoffin/dcraw/
  11. # 3) http://syscall.eu/#pana
  12. # 4) Iliah Borg private communication (LibRaw)
  13. # JD) Jens Duttke private communication (TZ3,FZ30,FZ50)
  14. #------------------------------------------------------------------------------
  15. package Image::ExifTool::PanasonicRaw;
  16. use strict;
  17. use vars qw($VERSION);
  18. use Image::ExifTool qw(:DataAccess :Utils);
  19. use Image::ExifTool::Exif;
  20. $VERSION = '1.09';
  21. sub ProcessJpgFromRaw($$$);
  22. sub WriteJpgFromRaw($$$);
  23. sub WriteDistortionInfo($$$);
  24. sub ProcessDistortionInfo($$$);
  25. my %jpgFromRawMap = (
  26. IFD1 => 'IFD0',
  27. EXIF => 'IFD0', # to write EXIF as a block
  28. ExifIFD => 'IFD0',
  29. GPS => 'IFD0',
  30. SubIFD => 'IFD0',
  31. GlobParamIFD => 'IFD0',
  32. PrintIM => 'IFD0',
  33. InteropIFD => 'ExifIFD',
  34. MakerNotes => 'ExifIFD',
  35. IFD0 => 'APP1',
  36. MakerNotes => 'ExifIFD',
  37. Comment => 'COM',
  38. );
  39. my %wbTypeInfo = (
  40. PrintConv => \%Image::ExifTool::Exif::lightSource,
  41. SeparateTable => 'EXIF LightSource',
  42. );
  43. # Tags found in Panasonic RAW/RW2/RWL images (ref PH)
  44. %Image::ExifTool::PanasonicRaw::Main = (
  45. GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'},
  46. WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
  47. CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
  48. WRITE_GROUP => 'IFD0', # default write group
  49. NOTES => 'These tags are found in IFD0 of Panasonic/Leica RAW, RW2 and RWL images.',
  50. 0x01 => {
  51. Name => 'PanasonicRawVersion',
  52. Writable => 'undef',
  53. },
  54. 0x02 => 'SensorWidth', #1/PH
  55. 0x03 => 'SensorHeight', #1/PH
  56. 0x04 => 'SensorTopBorder', #JD
  57. 0x05 => 'SensorLeftBorder', #JD
  58. 0x06 => 'SensorBottomBorder', #PH
  59. 0x07 => 'SensorRightBorder', #PH
  60. # observed values for unknown tags - PH
  61. # 0x08: 1
  62. # 0x09: 1,3,4
  63. # 0x0a: 12
  64. 0x08 => { #4
  65. Name => 'BlackLevel1',
  66. Writable => 'int16u',
  67. Notes => q{
  68. summing BlackLevel1+2+3 values gives the common bias that must be added to
  69. the BlackLevelRed/Green/Blue tags below
  70. },
  71. },
  72. 0x09 => { Name => 'BlackLevel2', Writable => 'int16u' }, #4
  73. 0x0a => { Name => 'BlackLevel3', Writable => 'int16u' }, #4
  74. # 0x0b: 0x860c,0x880a,0x880c
  75. # 0x0c: 2 (only Leica Digilux 2)
  76. # 0x0d: 0,1
  77. # 0x0e,0x0f,0x10: 4095
  78. 0x0e => { Name => 'LinearityLimitRed', Writable => 'int16u' }, #4
  79. 0x0f => { Name => 'LinearityLimitGreen', Writable => 'int16u' }, #4
  80. 0x10 => { Name => 'LinearityLimitBlue', Writable => 'int16u' }, #4
  81. 0x11 => { #JD
  82. Name => 'RedBalance',
  83. Writable => 'int16u',
  84. ValueConv => '$val / 256',
  85. ValueConvInv => 'int($val * 256 + 0.5)',
  86. Notes => 'found in Digilux 2 RAW images',
  87. },
  88. 0x12 => { #JD
  89. Name => 'BlueBalance',
  90. Writable => 'int16u',
  91. ValueConv => '$val / 256',
  92. ValueConvInv => 'int($val * 256 + 0.5)',
  93. },
  94. 0x13 => { #4
  95. Name => 'WBInfo',
  96. SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo' },
  97. },
  98. 0x17 => { #1
  99. Name => 'ISO',
  100. Writable => 'int16u',
  101. },
  102. # 0x18,0x19,0x1a: 0
  103. 0x18 => { #4
  104. Name => 'HighISOMultiplierRed',
  105. Writable => 'int16u',
  106. ValueConv => '$val / 256',
  107. ValueConvInv => 'int($val * 256 + 0.5)',
  108. },
  109. 0x19 => { #4
  110. Name => 'HighISOMultiplierGreen',
  111. Writable => 'int16u',
  112. ValueConv => '$val / 256',
  113. ValueConvInv => 'int($val * 256 + 0.5)',
  114. },
  115. 0x1a => { #4
  116. Name => 'HighISOMultiplierBlue',
  117. Writable => 'int16u',
  118. ValueConv => '$val / 256',
  119. ValueConvInv => 'int($val * 256 + 0.5)',
  120. },
  121. # 0x1b: [binary data] (something to do with the camera ISO cababilities: int16u count N,
  122. # followed by table of N entries: int16u ISO, int16u[3] RGB gains - ref 4)
  123. 0x1c => { Name => 'BlackLevelRed', Writable => 'int16u' }, #4
  124. 0x1d => { Name => 'BlackLevelGreen', Writable => 'int16u' }, #4
  125. 0x1e => { Name => 'BlackLevelBlue', Writable => 'int16u' }, #4
  126. 0x24 => { #2
  127. Name => 'WBRedLevel',
  128. Writable => 'int16u',
  129. },
  130. 0x25 => { #2
  131. Name => 'WBGreenLevel',
  132. Writable => 'int16u',
  133. },
  134. 0x26 => { #2
  135. Name => 'WBBlueLevel',
  136. Writable => 'int16u',
  137. },
  138. 0x27 => { #4
  139. Name => 'WBInfo2',
  140. SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::WBInfo2' },
  141. },
  142. # 0x27,0x29,0x2a,0x2b,0x2c: [binary data]
  143. # 0x2d: 2,3
  144. 0x2e => { #JD
  145. Name => 'JpgFromRaw', # (writable directory!)
  146. Groups => { 2 => 'Preview' },
  147. Writable => 'undef',
  148. # protect this tag because it contains all the metadata
  149. Flags => [ 'Binary', 'Protected', 'NestedHtmlDump', 'BlockExtract' ],
  150. Notes => 'processed as an embedded document because it contains full EXIF',
  151. WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)',
  152. DataTag => 'JpgFromRaw',
  153. RawConv => '$self->ValidateImage(\$val,$tag)',
  154. SubDirectory => {
  155. # extract information from embedded image since it is metadata-rich,
  156. # unless HtmlDump option set (note that the offsets will be relative,
  157. # not absolute like they should be in verbose mode)
  158. TagTable => 'Image::ExifTool::JPEG::Main',
  159. WriteProc => \&WriteJpgFromRaw,
  160. ProcessProc => \&ProcessJpgFromRaw,
  161. },
  162. },
  163. 0x2f => { Name => 'CropTop', Writable => 'int16u' },
  164. 0x30 => { Name => 'CropLeft', Writable => 'int16u' },
  165. 0x31 => { Name => 'CropBottom', Writable => 'int16u' },
  166. 0x32 => { Name => 'CropRight', Writable => 'int16u' },
  167. 0x10f => {
  168. Name => 'Make',
  169. Groups => { 2 => 'Camera' },
  170. Writable => 'string',
  171. DataMember => 'Make',
  172. # save this value as an ExifTool member variable
  173. RawConv => '$self->{Make} = $val',
  174. },
  175. 0x110 => {
  176. Name => 'Model',
  177. Description => 'Camera Model Name',
  178. Groups => { 2 => 'Camera' },
  179. Writable => 'string',
  180. DataMember => 'Model',
  181. # save this value as an ExifTool member variable
  182. RawConv => '$self->{Model} = $val',
  183. },
  184. 0x111 => {
  185. Name => 'StripOffsets',
  186. # (this value is 0xffffffff for some models, and RawDataOffset must be used)
  187. Flags => [ 'IsOffset', 'PanasonicHack' ],
  188. OffsetPair => 0x117, # point to associated byte counts
  189. ValueConv => 'length($val) > 32 ? \$val : $val',
  190. },
  191. 0x112 => {
  192. Name => 'Orientation',
  193. Writable => 'int16u',
  194. PrintConv => \%Image::ExifTool::Exif::orientation,
  195. Priority => 0, # so IFD1 doesn't take precedence
  196. },
  197. 0x116 => {
  198. Name => 'RowsPerStrip',
  199. Priority => 0,
  200. },
  201. 0x117 => {
  202. Name => 'StripByteCounts',
  203. # (note that this value may represent something like uncompressed byte count
  204. # for RAW/RW2/RWL images from some models, and is zero for some other models)
  205. OffsetPair => 0x111, # point to associated offset
  206. ValueConv => 'length($val) > 32 ? \$val : $val',
  207. },
  208. 0x118 => {
  209. Name => 'RawDataOffset', #PH (RW2/RWL)
  210. IsOffset => '$$et{TIFF_TYPE} =~ /^(RW2|RWL)$/', # (invalid in DNG-converted files)
  211. PanasonicHack => 1,
  212. OffsetPair => 0x117, # (use StripByteCounts as the offset pair)
  213. },
  214. 0x119 => {
  215. Name => 'DistortionInfo',
  216. SubDirectory => { TagTable => 'Image::ExifTool::PanasonicRaw::DistortionInfo' },
  217. },
  218. # 0x11b - chromatic aberration correction (ref 3)
  219. 0x2bc => { # PH Extension!!
  220. Name => 'ApplicationNotes', # (writable directory!)
  221. Writable => 'int8u',
  222. Format => 'undef',
  223. Flags => [ 'Binary', 'Protected' ],
  224. SubDirectory => {
  225. DirName => 'XMP',
  226. TagTable => 'Image::ExifTool::XMP::Main',
  227. },
  228. },
  229. 0x83bb => { # PH Extension!!
  230. Name => 'IPTC-NAA', # (writable directory!)
  231. Format => 'undef', # convert binary values as undef
  232. Writable => 'int32u', # but write int32u format code in IFD
  233. WriteGroup => 'IFD0',
  234. Flags => [ 'Binary', 'Protected' ],
  235. SubDirectory => {
  236. DirName => 'IPTC',
  237. TagTable => 'Image::ExifTool::IPTC::Main',
  238. },
  239. },
  240. 0x8769 => {
  241. Name => 'ExifOffset',
  242. Groups => { 1 => 'ExifIFD' },
  243. Flags => 'SubIFD',
  244. SubDirectory => {
  245. TagTable => 'Image::ExifTool::Exif::Main',
  246. DirName => 'ExifIFD',
  247. Start => '$val',
  248. },
  249. },
  250. 0x8825 => {
  251. Name => 'GPSInfo',
  252. Groups => { 1 => 'GPS' },
  253. Flags => 'SubIFD',
  254. SubDirectory => {
  255. DirName => 'GPS',
  256. TagTable => 'Image::ExifTool::GPS::Main',
  257. Start => '$val',
  258. },
  259. },
  260. # 0xffff => 'DCSHueShiftValues', #exifprobe (NC)
  261. );
  262. # white balance information (ref 4)
  263. # (PanasonicRawVersion<200: Digilux 2)
  264. %Image::ExifTool::PanasonicRaw::WBInfo = (
  265. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  266. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  267. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  268. WRITABLE => 1,
  269. FORMAT => 'int16u',
  270. FIRST_ENTRY => 0,
  271. 0 => 'NumWBEntries',
  272. 1 => { Name => 'WBType1', %wbTypeInfo },
  273. 2 => { Name => 'WB_RBLevels1', Format => 'int16u[2]' },
  274. 4 => { Name => 'WBType2', %wbTypeInfo },
  275. 5 => { Name => 'WB_RBLevels2', Format => 'int16u[2]' },
  276. 7 => { Name => 'WBType3', %wbTypeInfo },
  277. 8 => { Name => 'WB_RBLevels3', Format => 'int16u[2]' },
  278. 10 => { Name => 'WBType4', %wbTypeInfo },
  279. 11 => { Name => 'WB_RBLevels4', Format => 'int16u[2]' },
  280. 13 => { Name => 'WBType5', %wbTypeInfo },
  281. 14 => { Name => 'WB_RBLevels5', Format => 'int16u[2]' },
  282. 16 => { Name => 'WBType6', %wbTypeInfo },
  283. 17 => { Name => 'WB_RBLevels6', Format => 'int16u[2]' },
  284. 19 => { Name => 'WBType7', %wbTypeInfo },
  285. 20 => { Name => 'WB_RBLevels7', Format => 'int16u[2]' },
  286. );
  287. # white balance information (ref 4)
  288. # (PanasonicRawVersion>=200: D-Lux2, D-Lux3, DMC-FZ18/FZ30/LX1/L10)
  289. %Image::ExifTool::PanasonicRaw::WBInfo2 = (
  290. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  291. WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
  292. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  293. WRITABLE => 1,
  294. FORMAT => 'int16u',
  295. FIRST_ENTRY => 0,
  296. 0 => 'NumWBEntries',
  297. 1 => { Name => 'WBType1', %wbTypeInfo },
  298. 2 => { Name => 'WB_RGBLevels1', Format => 'int16u[3]' },
  299. 5 => { Name => 'WBType2', %wbTypeInfo },
  300. 6 => { Name => 'WB_RGBLevels2', Format => 'int16u[3]' },
  301. 9 => { Name => 'WBType3', %wbTypeInfo },
  302. 10 => { Name => 'WB_RGBLevels3', Format => 'int16u[3]' },
  303. 13 => { Name => 'WBType4', %wbTypeInfo },
  304. 14 => { Name => 'WB_RGBLevels4', Format => 'int16u[3]' },
  305. 17 => { Name => 'WBType5', %wbTypeInfo },
  306. 18 => { Name => 'WB_RGBLevels5', Format => 'int16u[3]' },
  307. 21 => { Name => 'WBType6', %wbTypeInfo },
  308. 22 => { Name => 'WB_RGBLevels6', Format => 'int16u[3]' },
  309. 25 => { Name => 'WBType7', %wbTypeInfo },
  310. 26 => { Name => 'WB_RGBLevels7', Format => 'int16u[3]' },
  311. );
  312. # lens distortion information (ref 3)
  313. # (distortion correction equation: Ru = scale*(Rd + a*Rd^3 + b*Rd^5 + c*Rd^7), ref 3)
  314. %Image::ExifTool::PanasonicRaw::DistortionInfo = (
  315. PROCESS_PROC => \&ProcessDistortionInfo,
  316. WRITE_PROC => \&WriteDistortionInfo,
  317. CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
  318. # (don't make this family 0 MakerNotes because we don't want it to be a deletable group)
  319. GROUPS => { 0 => 'PanasonicRaw', 1 => 'PanasonicRaw', 2 => 'Image'},
  320. WRITABLE => 1,
  321. FORMAT => 'int16s',
  322. FIRST_ENTRY => 0,
  323. NOTES => 'Lens distortion correction information.',
  324. # 0,1 - checksums
  325. 2 => {
  326. Name => 'DistortionParam02',
  327. ValueConv => '$val / 32768',
  328. ValueConvInv => '$val * 32768',
  329. },
  330. # 3 - usually 0, but seen 0x026b when value 5 is non-zero
  331. 4 => {
  332. Name => 'DistortionParam04',
  333. ValueConv => '$val / 32768',
  334. ValueConvInv => '$val * 32768',
  335. },
  336. 5 => {
  337. Name => 'DistortionScale',
  338. ValueConv => '1 / (1 + $val/32768)',
  339. ValueConvInv => '(1/$val - 1) * 32768',
  340. },
  341. # 6 - seen 0x0000-0x027f
  342. 7.1 => {
  343. Name => 'DistortionCorrection',
  344. Mask => 0x0f,
  345. # (have seen the upper 4 bits set for GF5 and GX1, giving a value of -4095 - PH)
  346. PrintConv => { 0 => 'Off', 1 => 'On' },
  347. },
  348. 8 => {
  349. Name => 'DistortionParam08',
  350. ValueConv => '$val / 32768',
  351. ValueConvInv => '$val * 32768',
  352. },
  353. 9 => {
  354. Name => 'DistortionParam09',
  355. ValueConv => '$val / 32768',
  356. ValueConvInv => '$val * 32768',
  357. },
  358. # 10 - seen 0xfc,0x0101,0x01f4,0x021d,0x0256
  359. 11 => {
  360. Name => 'DistortionParam11',
  361. ValueConv => '$val / 32768',
  362. ValueConvInv => '$val * 32768',
  363. },
  364. 12 => {
  365. Name => 'DistortionN',
  366. Unknown => 1,
  367. },
  368. # 13 - seen 0x0000,0x01f9-0x02b2
  369. # 14,15 - checksums
  370. );
  371. # PanasonicRaw composite tags
  372. %Image::ExifTool::PanasonicRaw::Composite = (
  373. ImageWidth => {
  374. Require => {
  375. 0 => 'IFD0:SensorLeftBorder',
  376. 1 => 'IFD0:SensorRightBorder',
  377. },
  378. ValueConv => '$val[1] - $val[0]',
  379. },
  380. ImageHeight => {
  381. Require => {
  382. 0 => 'IFD0:SensorTopBorder',
  383. 1 => 'IFD0:SensorBottomBorder',
  384. },
  385. ValueConv => '$val[1] - $val[0]',
  386. },
  387. );
  388. # add our composite tags
  389. Image::ExifTool::AddCompositeTags('Image::ExifTool::PanasonicRaw');
  390. #------------------------------------------------------------------------------
  391. # checksum algorithm for lens distortion correction information (ref 3)
  392. # Inputs: 0) data ref, 1) start position, 2) number of bytes, 3) incement
  393. # Returns: checksum value
  394. sub Checksum($$$$)
  395. {
  396. my ($dataPt, $start, $num, $inc) = @_;
  397. my $csum = 0;
  398. my $i;
  399. for ($i=0; $i<$num; ++$i) {
  400. $csum = (73 * $csum + Get8u($dataPt, $start + $i * $inc)) % 0xffef;
  401. }
  402. return $csum;
  403. }
  404. #------------------------------------------------------------------------------
  405. # Read lens distortion information
  406. # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
  407. # Returns: 1 on success
  408. sub ProcessDistortionInfo($$$)
  409. {
  410. my ($et, $dirInfo, $tagTablePtr) = @_;
  411. my $dataPt = $$dirInfo{DataPt};
  412. my $start = $$dirInfo{DirStart} || 0;
  413. my $size = $$dirInfo{DataLen} || (length($$dataPt) - $start);
  414. if ($size == 32) {
  415. # verify the checksums (ref 3)
  416. my $csum1 = Checksum($dataPt, $start + 4, 12, 1);
  417. my $csum2 = Checksum($dataPt, $start + 16, 12, 1);
  418. my $csum3 = Checksum($dataPt, $start + 2, 14, 2);
  419. my $csum4 = Checksum($dataPt, $start + 3, 14, 2);
  420. my $res = $csum1 ^ Get16u($dataPt, $start + 2) ^
  421. $csum2 ^ Get16u($dataPt, $start + 28) ^
  422. $csum3 ^ Get16u($dataPt, $start + 0) ^
  423. $csum4 ^ Get16u($dataPt, $start + 30);
  424. $et->Warn('Invalid DistortionInfo checksum',1) if $res;
  425. } else {
  426. $et->Warn('Invalid DistortionInfo',1);
  427. }
  428. return $et->ProcessBinaryData($dirInfo, $tagTablePtr);
  429. }
  430. #------------------------------------------------------------------------------
  431. # Write lens distortion information
  432. # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
  433. # Returns: updated distortion information or undef on error
  434. sub WriteDistortionInfo($$$)
  435. {
  436. my ($et, $dirInfo, $tagTablePtr) = @_;
  437. $et or return 1; # (allow dummy access)
  438. my $dat = $et->WriteBinaryData($dirInfo, $tagTablePtr);
  439. if (defined $dat and length($dat) == 32) {
  440. # fix checksums (ref 3)
  441. Set16u(Checksum(\$dat, 4, 12, 1), \$dat, 2);
  442. Set16u(Checksum(\$dat, 16, 12, 1), \$dat, 28);
  443. Set16u(Checksum(\$dat, 2, 14, 2), \$dat, 0);
  444. Set16u(Checksum(\$dat, 3, 14, 2), \$dat, 30);
  445. } else {
  446. $et->Warn('Error wriing DistortionInfo',1);
  447. }
  448. return $dat;
  449. }
  450. #------------------------------------------------------------------------------
  451. # Patch for writing non-standard Panasonic RAW/RW2/RWL raw data
  452. # Inputs: 0) offset info ref, 1) raf ref, 2) IFD number
  453. # Returns: error string, or undef on success
  454. # OffsetInfo is a hash by tag ID of lists with the following elements:
  455. # 0 - tag info ref
  456. # 1 - pointer to int32u offset in IFD or value data
  457. # 2 - value count
  458. # 3 - reference to list of original offset values
  459. # 4 - IFD format number
  460. sub PatchRawDataOffset($$$)
  461. {
  462. my ($offsetInfo, $raf, $ifd) = @_;
  463. my $stripOffsets = $$offsetInfo{0x111};
  464. my $stripByteCounts = $$offsetInfo{0x117};
  465. my $rawDataOffset = $$offsetInfo{0x118};
  466. my $err;
  467. $err = 1 unless $ifd == 0;
  468. $err = 1 unless $stripOffsets and $stripByteCounts and $$stripOffsets[2] == 1;
  469. if ($rawDataOffset) {
  470. $err = 1 unless $$rawDataOffset[2] == 1;
  471. $err = 1 unless $$stripOffsets[3][0] == 0xffffffff or $$stripByteCounts[3][0] == 0;
  472. }
  473. $err and return 'Unsupported Panasonic/Leica RAW variant';
  474. if ($rawDataOffset) {
  475. # update StripOffsets along with this tag if it contains a reasonable value
  476. unless ($$stripOffsets[3][0] == 0xffffffff) {
  477. # save pointer to StripOffsets value for updating later
  478. push @$rawDataOffset, $$stripOffsets[1];
  479. }
  480. # handle via RawDataOffset instead of StripOffsets
  481. $stripOffsets = $$offsetInfo{0x111} = $rawDataOffset;
  482. delete $$offsetInfo{0x118};
  483. }
  484. # determine the length of the raw data
  485. my $pos = $raf->Tell();
  486. $raf->Seek(0, 2) or $err = 1; # seek to end of file
  487. my $len = $raf->Tell() - $$stripOffsets[3][0];
  488. $raf->Seek($pos, 0);
  489. # quick check to be sure the raw data length isn't unreasonable
  490. # (the 22-byte length is for '<Dummy raw image data>' in our tests)
  491. $err = 1 if ($len < 1000 and $len != 22) or $len & 0x80000000;
  492. $err and return 'Error reading Panasonic raw data';
  493. # update StripByteCounts info with raw data length
  494. # (note that the original value is maintained in the file)
  495. $$stripByteCounts[3][0] = $len;
  496. return undef;
  497. }
  498. #------------------------------------------------------------------------------
  499. # Write meta information to Panasonic JpgFromRaw in RAW/RW2/RWL image
  500. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  501. # Returns: updated image data, or undef if nothing changed
  502. sub WriteJpgFromRaw($$$)
  503. {
  504. my ($et, $dirInfo, $tagTablePtr) = @_;
  505. my $dataPt = $$dirInfo{DataPt};
  506. my $byteOrder = GetByteOrder();
  507. my $fileType = $$et{FILE_TYPE}; # RAW, RW2 or RWL
  508. my $dirStart = $$dirInfo{DirStart};
  509. if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL
  510. my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart;
  511. my $buff = substr($$dataPt, $dirStart, $dirLen);
  512. $dataPt = \$buff;
  513. }
  514. my $raf = new File::RandomAccess($dataPt);
  515. my $outbuff;
  516. my %dirInfo = (
  517. RAF => $raf,
  518. OutFile => \$outbuff,
  519. );
  520. $$et{BASE} = $$dirInfo{DataPos};
  521. $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG';
  522. # use a specialized map so we don't write XMP or IPTC (or other junk) into the JPEG
  523. my $editDirs = $$et{EDIT_DIRS};
  524. my $addDirs = $$et{ADD_DIRS};
  525. $et->InitWriteDirs(\%jpgFromRawMap);
  526. # don't add XMP segment (IPTC won't get added because it is in Photoshop record)
  527. delete $$et{ADD_DIRS}{XMP};
  528. my $result = $et->WriteJPEG(\%dirInfo);
  529. # restore variables we changed
  530. $$et{BASE} = 0;
  531. $$et{FILE_TYPE} = 'TIFF';
  532. $$et{TIFF_TYPE} = $fileType;
  533. $$et{EDIT_DIRS} = $editDirs;
  534. $$et{ADD_DIRS} = $addDirs;
  535. SetByteOrder($byteOrder);
  536. return $result > 0 ? $outbuff : $$dataPt;
  537. }
  538. #------------------------------------------------------------------------------
  539. # Extract meta information from an Panasonic JpgFromRaw
  540. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  541. # Returns: 1 on success, 0 if this wasn't a valid JpgFromRaw image
  542. sub ProcessJpgFromRaw($$$)
  543. {
  544. my ($et, $dirInfo, $tagTablePtr) = @_;
  545. my $dataPt = $$dirInfo{DataPt};
  546. my $byteOrder = GetByteOrder();
  547. my $fileType = $$et{FILE_TYPE}; # RAW, RW2 or RWL
  548. my $tagInfo = $$dirInfo{TagInfo};
  549. my $verbose = $et->Options('Verbose');
  550. my ($indent, $out);
  551. $tagInfo or $et->Warn('No tag info for Panasonic JpgFromRaw'), return 0;
  552. my $dirStart = $$dirInfo{DirStart};
  553. if ($dirStart) { # DirStart is non-zero in DNG-converted RW2/RWL
  554. my $dirLen = $$dirInfo{DirLen} | length($$dataPt) - $dirStart;
  555. my $buff = substr($$dataPt, $dirStart, $dirLen);
  556. $dataPt = \$buff;
  557. }
  558. $$et{BASE} = $$dirInfo{DataPos} + ($dirStart || 0);
  559. $$et{FILE_TYPE} = $$et{TIFF_TYPE} = 'JPEG';
  560. $$et{DOC_NUM} = 1;
  561. # extract information from embedded JPEG
  562. my %dirInfo = (
  563. Parent => 'RAF',
  564. RAF => new File::RandomAccess($dataPt),
  565. );
  566. if ($verbose) {
  567. my $indent = $$et{INDENT};
  568. $$et{INDENT} = ' ';
  569. $out = $et->Options('TextOut');
  570. print $out '--- DOC1:JpgFromRaw ',('-'x56),"\n";
  571. }
  572. my $rtnVal = $et->ProcessJPEG(\%dirInfo);
  573. # restore necessary variables for continued RW2/RWL processing
  574. $$et{BASE} = 0;
  575. $$et{FILE_TYPE} = 'TIFF';
  576. $$et{TIFF_TYPE} = $fileType;
  577. delete $$et{DOC_NUM};
  578. SetByteOrder($byteOrder);
  579. if ($verbose) {
  580. $$et{INDENT} = $indent;
  581. print $out ('-'x76),"\n";
  582. }
  583. return $rtnVal;
  584. }
  585. 1; # end
  586. __END__
  587. =head1 NAME
  588. Image::ExifTool::PanasonicRaw - Read/write Panasonic/Leica RAW/RW2/RWL meta information
  589. =head1 SYNOPSIS
  590. This module is loaded automatically by Image::ExifTool when required.
  591. =head1 DESCRIPTION
  592. This module contains definitions required by Image::ExifTool to read and
  593. write meta information in Panasonic/Leica RAW, RW2 and RWL images.
  594. =head1 AUTHOR
  595. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  596. This library is free software; you can redistribute it and/or modify it
  597. under the same terms as Perl itself.
  598. =head1 REFERENCES
  599. =over 4
  600. =item L<http://www.cybercom.net/~dcoffin/dcraw/>
  601. =back
  602. =head1 SEE ALSO
  603. L<Image::ExifTool::TagNames/PanasonicRaw Tags>,
  604. L<Image::ExifTool(3pm)|Image::ExifTool>
  605. =cut