Created
April 13, 2015 09:10
-
-
Save miguelrgonzalez/d8daf7e3840f20b8dcee to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
xquery version "1.0-ml"; | |
module namespace exif = "http://marklogic.com/exif"; | |
declare variable $FIELDS := | |
map:new(( | |
map:entry("010D", "DocumentName"), | |
map:entry("011A", "XResolution"), | |
map:entry("011B", "YResolution"), | |
map:entry("0128", "ResolutionUnit"), | |
map:entry("0213", "YCbCrPositioning"), | |
map:entry("8769", "ExifOffset"), | |
map:entry("9000", "ExifVersion"), | |
map:entry("9101", "ComponentsConfiguration"), | |
map:entry("A000", "FlashpixVersion"), | |
map:entry("A001", "ColorSpace"), | |
map:entry("A420", "ImageUniqueID"), | |
map:entry("A431", "BodySerialNumber") | |
)); | |
declare variable $TYPES := | |
<types> | |
<type id="1" size="1">Byte</type> | |
<type id="2" size="1" decode="true">ASCII</type> | |
<type id="3" size="2">Short</type> | |
<type id="4" size="4">Long</type> | |
<type id="5" size="8">Rational</type> | |
<type id="6" size="1">SBYTE</type> | |
<type id="7" size="1" decode="true">Undefinned</type> | |
<type id="8" size="2">SShort</type> | |
<type id="9" size="4">SLong</type> | |
<type id="10" size="8">SRational</type> | |
<type id="11" size="4">Float</type> | |
<type id="12" size="8">Double</type> | |
</types>; | |
declare variable $FIELD-SIZE := 12; | |
declare function exif:endianness($binary as binary(), $byte-order as xs:string) as binary() | |
{ | |
if ($byte-order eq 'LI') | |
then $binary | |
else | |
(: BI :) | |
let $size := xdmp:binary-size($binary) | |
(: See bug #33099. Whenever the bug is fixed, this will fail :) | |
let $binary := binary { concat(string($binary), "00")} | |
let $result := | |
for $pos in (0 to $size -1) | |
return string(xdmp:subbinary($binary, $size - $pos, 1)) | |
return binary {xs:hexBinary(string-join($result, ''))} | |
}; | |
declare function exif:process-field($field as binary(), | |
$byte-order as xs:string, | |
$image as binary(), | |
$tiff-offset as xs:integer) as element()? | |
{ | |
let $tag-id := xdmp:subbinary($field, 1, 2) | |
let $type-id := xdmp:hex-to-integer(string(xs:hexBinary(xs:string(xdmp:subbinary($field, 3, 2))))) | |
let $count := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($field, 5, 4)))) | |
let $offset-to-value := xdmp:subbinary($field, 9, 4) | |
return | |
element field { | |
attribute original { xs:string($field) }, | |
attribute count { $count }, | |
attribute name { map:get($FIELDS, xs:string($tag-id)) }, | |
attribute tag-id { xs:string($tag-id) }, | |
attribute type { $TYPES/type[@id eq $type-id]/text() }, | |
attribute offset { xs:hexBinary($offset-to-value) }, | |
exif:fetch-value($image, | |
$byte-order, | |
$tiff-offset, | |
$offset-to-value, | |
$type-id, | |
$count) | |
} | |
}; | |
declare function exif:fetch-value($image as binary(), | |
$byte-order as xs:string, | |
$tiff-offset as xs:integer, | |
$offset as binary(), | |
$type as xs:integer, | |
$count as xs:integer) as item()? | |
{ | |
if ($TYPES/type[@id eq $type] ne 'ExifOffset') | |
then | |
if ($count * $TYPES/type[@id eq $type]/@size > 4) | |
(: if the value is bigger than 4 bytes will be stored in the data section :) | |
then | |
let $binary := xdmp:subbinary($image, | |
$tiff-offset + xdmp:hex-to-integer(string(xs:hexBinary($offset))), | |
$count * $TYPES/type[@id eq $type]/@size) | |
return | |
if (xdmp:binary-size($binary) > 0) | |
then | |
if ($TYPES/type[@id eq $type and @decode eq 'true']) | |
then xdmp:binary-decode($binary, 'utf8') | |
else xs:string(data($binary)) | |
else '' | |
else | |
if ($TYPES/type[@id eq $type and @decode eq 'true']) | |
then xdmp:binary-decode($offset, 'utf8') | |
else xs:string(data($offset)) | |
else '' | |
}; | |
declare function exif:process-fields($image as binary(), | |
$byte-order as xs:string, | |
$tiff-offset as xs:integer, | |
$offset as xs:integer) as element()* | |
{ | |
let $field-count := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($image, $tiff-offset + $offset, 2)))) | |
let $fields := | |
for $field in 0 to $field-count - 1 | |
return exif:process-field(binary { xdmp:subbinary($image, $tiff-offset + $offset + 2 + ($FIELD-SIZE* $field), $FIELD-SIZE) }, $byte-order, $image, $tiff-offset) | |
return | |
if ($fields[@name eq 'ExifOffset']) | |
(: There are more fields. Process them :) | |
then ( $fields, exif:process-fields($image, $byte-order, $tiff-offset, xdmp:hex-to-integer($fields[@name eq 'ExifOffset']/@offset))) | |
else $fields | |
}; | |
declare function exif:get-exif-fields($image as binary()) as element() | |
{ | |
let $magic-number := xs:hexBinary(xdmp:subbinary($image, 1, 2)) | |
(: See http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf :) | |
return | |
<fields> | |
{ | |
if(string($magic-number) eq 'FFD8') | |
then | |
( | |
(: it's a jpeg :) | |
let $app0-marker := string(xs:hexBinary(xdmp:subbinary($image, 3, 2))) | |
let $app0-size := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($image, 5, 2)))) | |
(: app1 block contains Exif Attribute Information :) | |
let $app1-marker := string(xs:hexBinary(xdmp:subbinary($image, $app0-size + 5, 2))) | |
let $app1-block-start := $app0-size + 7 | |
let $app1-size := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($image, $app1-block-start, 2)))) | |
let $app1-block := string(xs:hexBinary(xdmp:subbinary($image, $app1-block-start, $app1-size))) | |
return | |
(: http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf Page 89 | |
Checking for Exif ID code :) | |
if (matches($app1-block, '^....457869660000.*')) | |
then | |
( | |
let $tiff-header-start := $app1-block-start + 8 | |
let $tiff-header := string(xs:hexBinary(xdmp:subbinary($image, $tiff-header-start, $app1-size))) | |
let $byte-order := | |
if (matches($tiff-header, '^4D4D00.*')) | |
then | |
(: Big indian :) | |
( | |
'BI' | |
) | |
else | |
(: Assume 4949 Little indian :) | |
('LI') | |
(: see http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf :) | |
let $ifd0-offset := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($image, $tiff-header-start + 4, 4)))) | |
let $field-count := xdmp:hex-to-integer(string(xs:hexBinary(xdmp:subbinary($image, $tiff-header-start + $ifd0-offset, 2)))) | |
return | |
exif:process-fields($image, $byte-order, $tiff-header-start, $ifd0-offset) | |
) | |
(: App1 does not contain Exif information :) | |
else () | |
) | |
(: Not a jpeg :) | |
else () | |
} | |
</fields> | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment