#!/bin/bash # # jpegxmp -- set or add a number of common metadata properties to a JPEG file # # See --help (the function print-help) for a brief manual. # # Requires rdjpgxmp, wrjpgxmp, and xmptool, see # http://www.nbwuij.icu/People/Bos/JPEG-XMP/ # # Created: 3 November 2008 # Author: Bert Bos # Dublin Core properties # DC="http://purl.org/dc/elements/1.1/" TITLE="${DC}title" CREATOR="${DC}creator" SUBJECT="${DC}subject" DESCRIPTION="${DC}description" PUBLISHER="${DC}publisher" CONTRIBUTOR="${DC}contributor" DATE="${DC}date" TYPE="${DC}type" FORMAT="${DC}format" IDENTIFIER="${DC}identifier" RELATION="${DC}relation" COVERAGE="${DC}coverage" RIGHTS="${DC}rights" SOURCE="${DC}source" # PhotoRDF Technical properties # TECH="http://www.nbwuij.icu/2000/PhotoRDF/technical-1-0#" CAMERA="${TECH}camera" FILM="${TECH}film" LENS="${TECH}lens" DEVEL_DATE="${TECH}devel-date" # Selected EXIF/XMP properties # EXIF="http://ns.adobe.com/exif/1.0/" GPSVersionID="${EXIF}GPSVersionID" # Value should be 2.0.0.0 GPSLatitude="${EXIF}GPSLatitude" # DDD,MM,SSk or DDD,MM.mmk GPSLongitude="${EXIF}GPSLongitude" # DDD,MM,SSk or DDD,MM.mmk GPSAltitudeRef="${EXIF}GPSAltitudeRef" # 0 = above sea level, 1 = below GPSAltitude="${EXIF}GPSAltitude" # In meters GPSBearingRef="${EXIF}GPSDestBearingRef" # T (=True) or M (=Magnetic) North GPSBearing="${EXIF}GPSDestBearing" # Compas direction [0.0,360.0) # Command line options # OPTS="language:,title:,creator:,subject:,description:,publisher:,\ contributor:,date:,identifier:,relation:,coverage:,rights:,camera:,\ film:,lens:,development-date:,latitude:,longitude:,altitude:,\ source:,bearing:,bearingref:,timezone:,\ from-exif,from-name:,add,overwrite,edit,list,help" # sed option to get extended regexps # if sed -r p /dev/null; then ext="-r"; else ext="-E"; fi # die -- print error message and exit function die { echo "$1" >&2 exit 1 } # usage -- print usage message and exit function usage { echo "Usage: ${0##*/} options JPEG-file [JPEG-file...]" echo "where options are: --language= --title= --creator= --subject=" echo " --description= --publisher= --contributor= --date= --identifier=" echo " --relation= --coverage= --rights= --camera= --film= --lens=" echo " --development-date= --source= --latitude= --longitude=" echo " --altitude= --bearing= --bearingref= --timezone=" echo " --from-exif --from-name= --add --overwrite --edit --list --help" exit 1 } # print-help -- print a brief usage manual function print-help { local dq='"' sq="'" cat <<-EOD SYNOPSIS: ${0##*/} options [--] JPEG-file [JPEG-file...] ${0##*/} adds or replaces a number of common metadata properties in JPEG/JFIF files, such as the creator of the image, the location, the date, the copyright, the title and the description. The metadata is stored in XMP-format inside the image. OPTIONS: --language=L Set the language for the next arguments to L. Default is ${dq}x-default${dq}. The language should be code from BCP 47 (currently equal to RFC 4646), such as ${dq}en${dq} (for English) or ${dq}fr-ca${dq} (for Canadian French). --title=T Set the title to T. This should be a (short) text. --creator=C Set the creator to C. The creator is the person who took the photo (in the case of a photo) or otherwise made the image. --subject=keywords Set the subject. The value should be one or more comma-separated keywords. --description=D Set the description to D. This should be a free-form text. --publisher=P Set the publisher to P. --contributor=C Set the contributor to C. The contributor is somebody who contributed to the image, but is not the creator. --date=D Set the date to D. Suggested format: YYYY-MM-DD --identifier=I Set the image${sq}s number or identifier to I. This can serve as a unique reference for this image within a certain context, e.g., all images of the same publisher. Typically, multiple copies of the same image have the same identifier, but modified copies (e.g., cropped, resized or digitally processed) have different identifiers. The identifier need not be the same as the file name. --relation=R Set the relation between this image and other resources to R. Typically this identifies an event or common theme for a collection of related resources, e.g., ${dq}John${sq}s 17th birthday${dq} or ${dq}Experiment 1703${dq}. --coverage=C Set the (spatial) coverage of the image to C. Typically this is the location that is depicted in the image, e.g., ${dq}Amsterdam, The Netherlands${dq}. --rights=R Set the copyright statement to R. --camera=C Set the (kind of) camera to C, in case the image is a photo. Typically this is the brand and type, e.g., ${dq}Canon EOS D60${dq}. It may also be a scanner, if the image is scanned. --film=F Set the kind of film, in case the image is a photo taken on film. Suggested value for digital photos: ${dq}Digital${dq} --lens=L Set the kind of lens to L, in case the image is a photo. This could be the brand and type of the lens. --development-date=D Set the date the film was developed, in case the image is a photo taken on film. Suggested format: YYYY-MM-DD --source=S The source from which this image is derived. --latitude=L The latitude in one of three formats: (a) decimal ("-43.2675") where positive means north; (b) degrees, minutes, seconds and the letter N or S ("43,41,51N"); (c) degrees, minutes with decimals and the letter N or S ("43,41.85N"). Leading zeros on the degrees, minutes or seconds are optional. --longitude=L The longitude, in one of three formats: (a) decimal ("-43.2675") where positive means east; (b) degrees, minutes, seconds and the letter E or W ("6,59,3E"); (c) degrees, minutes with decimals and the letter E or W ("6,59.05E"). Leading zeros on the degrees, minutes or seconds are optional. --bearing=B Compass direction in which the picture was taken [0.0,360.0). --bearingref=[T|M] Reference for --bearing. Must be either "T" (true north) or "M" (magneting north). --timezone=tz When reading a date from the EXIF (see --from-exif), add this timezone. Format should be like +02:00 or -05:00, or Z for UTC. This must be set before --from-exif. --from-exif Try to copy the date (--date), camera (--camera), and position (--latitude, --longitude, --altitude, --bearing) from the EXIF data in the image, if any. --from-name=pattern Set the identifier (--identifier) to a part of the file name, as given by the pattern. The pattern must contain at least one pair of parentheses ${dq}(...)${dq} to indicate which part of the pattern is the identifier. The pattern is implicitly anchored at the start of the file name. Example: --from-name=${dq}([0-9]*)${dq} takes the sequence of digits at the start of the name as the identifier, so that, e.g., the file name ${dq}123456_JohnB.jpg${dq} gets the identifier ${dq}123456${dq}. --add Add rather than replace properties. Applies to the following arguments. The properties in the next options will be added, even if there are already values for the same property in the same language. Thus it is possible to give multiple values for some properties. (Some XMP software may not accept multiple values for certain properties.) --overwrite Replace rather than add. Applies to the following arguments. If the image already has a property with the same name and same language, it will be replaced. This is the default. --edit Extract XMP data, open an editor (\$EDITOR) to edit it, then put the edited data back. Warning:聽if there are errors in the data after editing, some or all of the data may be lost without warning. --list List the known fields from the image (in the given language, if --language precedes --list). --help Display this manual. All options may occur multiple times. Option names may be abbreviated. To delete a value, set it to the empty string (only in --overwrite mode; adding an empty value in --add mode has no effect). Tip: the XMP data is always read and written, so running the command without any options has the effect of ${dq}prettyprinting${dq} the XMP data (and removing any data the program doesn${sq}t understand...) EOD exit } # set-field -- set field $1 to value $2 with language $3 function set-field { local h r # Guess that a value that starts with http:/https: is a resource case "$2" in http:*|https:*) r=-r;; *) r=;; esac if [ "$mode" == "add" ]; then xmptool -w -l "$3" $r -- "$1" "$2" $TMP1 elif [ -z "$2" ]; then xmptool -d -l "$3" -- "$1" $TMP1 else xmptool -d -l "$3" -- "$1" $TMP1 | xmptool -w -l "$3" $r -- "$1" "$2" fi >$TMP2 h=$TMP1 TMP1=$TMP2 TMP2=$h } # set-altitude -- set altitude to value $1 function set-altitude { local ref alt case "$1" in -*) ref=1; alt=${1#-};; +*) ref=0; alt=${1#+};; *) ref=0; alt=$1;; esac set-field "$GPSAltitudeRef" "$ref" x-default set-field "$GPSAltitude" "$alt" x-default set-field "$GPSVersionID" "2.0.0.0" x-default } # set-lat-or-long -- set latitude or longitude after checking the syntax function set-lat-or-long { local d m if [ "$2" == "" ]; then set-field "$1" "" x-default elif [[ "$1" == "$GPSLatitude" ]]; then if [[ "$2" =~ ^[0-9]+,[0-9]+,?[0-9]*\.?[0-9]*[NS]$ ]]; then # ^[0-9]+,[0-9]+(,[0-9]+)?(\.[0-9]+)?[NS]$ set-field "$1" "$2" x-default elif [[ "$2" =~ ^[0-9]+\.?[0-9]*$ ]]; then # ^[0-9]+(\.[0-9]+)?$ m=$(dc <<<"$2 1%4k60*p"); d=${2%.*} set-field "$1" "${d},${m}N" x-default elif [[ "$2" =~ ^-[0-9]+\.?[0-9]*$ ]]; then # ^-[0-9]+(\.[0-9]+)?$ m=$(dc <<<"${2#-} 1%4k60*p"); d=${2#-}; d=${d%.*} set-field "$1" "${d},${m}S" x-default elif [[ "$2" =~ ^[0-9]+,[0-9]*\.[0-9]+[NS]$ ]]; then set-field "$1" "$2" x-default else die "$0: Error: lat/long must be like 6,58,6W or 43,58.1N or -7.956" fi elif [[ "$1" == "$GPSLongitude" ]]; then if [[ "$2" =~ ^[0-9]+,[0-9]+,?[0-9]*\.?[0-9]*[EW]$ ]]; then # ^[0-9]+,[0-9]+(,[0-9]+)?(\.[0-9]+)?[EW]$ set-field "$1" "$2" x-default elif [[ "$2" =~ ^[0-9]+\.?[0-9]*$ ]]; then # ^[0-9]+(\.[0-9]+)?$ m=$(dc <<<"$2 1%4k60*p"); d=${2%.*} set-field "$1" "${d},${m}E" x-default elif [[ "$2" =~ ^-[0-9]+\.?[0-9]*$ ]]; then # ^-[0-9]+(\.[0-9]+)?$ m=$(dc <<<"${2#-} 1%4k60*p"); d=${2#-}; d=${d%.*} set-field "$1" "${d},${m}W" x-default elif [[ "$2" =~ ^[0-9]+,[0-9]*\.[0-9]+[EW]$ ]]; then set-field "$1" "$2" x-default else die "$0: Error: lat/long must be like 6,58,6W or 43,58.1N or -7.956" fi else die "$0: Cannot happen!" fi set-field "$GPSVersionID" "2.0.0.0" x-default } # set-bearingref -- set bearing reference direction after checking syntax function set-bearingref { case "$1" in "") set-field "$GPSBearingRef" "" x-default;; T|t) set-field "$GPSBearingRef" T x-default;; M|m) set-field "$GPSBearingRef" M x-default;; *) die "$0: Error: --bearingref must be \"T\" or \"M\"";; esac } # set-bearing -- set bearing after checking syntax function set-bearing { local n=${1%.*} local h=${1#$n} local d=1 if [ "$1" == "" ]; then set-field "$GPSBearing" "" x-default else case "$n" in [0-9]|[0-9][0-9]|[0-9][0-9][0-9]) ;; *) die "$0: Error Bearing value must be a number";; esac h=${h#.} n=${n}${h} while [ ! -z "$h" ]; do case "$h" in [0-9]*) d=${d}0; h=${h#?};; *) die "$0: Error Bearing value must be a number";; esac done set-field "$GPSBearing" $n/$d x-default fi } # copy_from_exif -- get date and camera from EXIF data in $1 function copy_from_exif { local inf=`jhead -v "$1"` local camera=`echo "$inf"|sed -n -e '/^Camera model/{s/[^:]*: *//p;q;}'` local date=`echo "$inf"|sed -n -e '/^Date\/Time/{s/[^:]*: *//p;q;}'` local latref=`echo "$inf"|sed -n -e '/^ *GPSLatitudeRef *=/{s/[^=]*=//p;q;}'` local latitude=`echo "$inf"|sed -n -e '/^ *GPSLatitude *=/{s/[^=]*=//p;q;}'` local longref=`echo "$inf"|sed -n -e '/^ *GPSLongitudeRef *=/{s/[^=]*=//p;q;}'` local longitude=`echo "$inf"|sed -n -e '/^ *GPSLongitude *=/{s/[^=]*=//p;q;}'` local altref=`echo "$inf"|sed -n -e '/^ *GPSAltitudeRef *=/{s/[^=]*=//p;q;}'` local altitude=`echo "$inf"|sed -n -e '/^ *GPSAltitude *=/{s/[^=]*=//p;q;}'` local bearing=`echo "$inf"|sed -n -e '/^ *GPSDestBearing *=/{s/[^=]*=//p;q;}'` local bearingref=`echo "$inf"|sed -n -e '/^ *GPSDestBearingRef *=/{s/[^=]*=//p;q;}'` if [ ! -z "$camera" ]; then set-field "$CAMERA" "$camera" x-default fi if [ ! -z "$date" ]; then date=${date/:/-}; date=${date/:/-} # 2019:07:20 -> 2019-07-20 set-field "$DATE" "$date$timezone" x-default fi if [ ! -z "$altitude" ]; then local a=${altitude%/*} b=${altitude#*/} case "$altref" in *1*) set-field "$GPSAltitude" -`echo "10k$a $b/p" | dc` x-default;; *) set-field "$GPSAltitude" `echo "10k$a $b/p" | dc` x-default;; esac fi if [ ! -z "$latitude" ]; then local d1 d2 m1 m2 s1 s2 h=$latitude d1=${h%%/*}; h=${h#$d1/} d2=${h%%,*}; h=${h#$d2, } m1=${h%%/*}; h=${h#$m1/} m2=${h%%,*}; h=${h#$m2, } s1=${h%%/*}; h=${h#$s1/} s2=${h%%,*} d=`echo "$d1 $d2/p" | dc` m=`echo "10k $m1 $m2 / $s1 $s2 / 60 / + p" | dc` set-field "$GPSLatitude" $d,$m${latref//\"/} x-default fi if [ ! -z "$longitude" ]; then local d1 d2 m1 m2 s1 s2 h=$longitude d1=${h%%/*}; h=${h#$d1/} d2=${h%%,*}; h=${h#$d2, } m1=${h%%/*}; h=${h#$m1/} m2=${h%%,*}; h=${h#$m2, } s1=${h%%/*}; h=${h#$s1/} s2=${h%%,*} d=`echo "$d1 $d2/p" | dc` m=`echo "10k $m1 $m2 / $s1 $s2 / 60 / + p" | dc` set-field "$GPSLongitude" $d,$m${longref//\"/} x-default fi if [ ! -z "$bearing" ]; then local n=${bearing%%/*} d=#{bearing##*/} set-field "$GPSBearing" `echo "10k $n $d / p" | dc` x-default fi if [ ! -z "$bearingref" ]; then set-field "$GPSBearing" ${bearingref//\"/} x-default fi } # from-name -- use part of $2 (given by pattern $1) as identifier function from-name { local ident ident=`basename "$2" | sed $ext -e "s/^$1.*/\\1/"` set-field "$IDENTIFIER" "$ident" x-default } # list-known-fields -- list the known fields in language $1, one per line function list-known-fields { local f n lang val if [ ! "$1" = "x-default" ]; then lang="-l $1"; fi for f in $TITLE $CREATOR $SUBJECT $DESCRIPTION $PUBLISHER $CONTRIBUTOR \ $DATE $IDENTIFIER $RELATION $COVERAGE $RIGHTS $SOURCE $CAMERA $FILM \ $LENS $DEVEL_DATE $GPSVersionID $GPSLatitude $GPSLongitude \ $GPSAltitudeRef $GPSAltitude $GPSBearingRef $GPSBearing; do val=`xmptool $lang -- $f $TMP1 | tr '\n' ' '` if [ ! -z "$val" ]; then n=${f#${DC}} n=${n#${TECH}} n=${n#${EXIF}} printf "%-8s %s\n" "$n" "$val" fi done } # Main LC_ALL=C # Avoid problems with sed LANG=C # Avoid problems with sed # Create some temporary files # trap 'rm -f $TMP1 $TMP2' 0 TMP1=`mktemp /tmp/jpegtmp.XXXXXX` || die "Could not create temporary file." TMP2=`mktemp /tmp/jpegtmp.XXXXXX` || die "Could not create temporary file." # Check prerequisites # JPEG_XMP="http://www.nbwuij.icu/People/Bos/JPEG-XMP/" JHEAD="http://www.sentex.net/~mwandel/jhead/" if ! type -t xmptool >/dev/null; then die "Need xmptool; see $JPEG_XMP"; fi if ! type -t rdjpgxmp >/dev/null; then die "Need rdjpgxmp; see $JPEG_XMP"; fi if ! type -t wrjpgxmp >/dev/null; then die "Need wrjpgxmp; see $JPEG_XMP"; fi if ! type -t jhead >/dev/null; then die "Need jhead; see $JHEAD"; fi if getopt -T >/dev/null || [ $? != 4 ]; then die "Need GNU version of getopt"; fi # Check and normalize the (long) command line options # h=`getopt -l $OPTS -q -- "" "$@"` if [ $? != 0 ]; then usage; fi eval set -- "$h" # Special rule for lonely --help # if [ "$1" == "--help" ]; then print-help; fi # Find the file arguments at the end of the options, after "--". # declare -i file=1 while [ "${!file}" != "--" ]; do let "file++"; done let "file++" if [ $file -gt $# ]; then usage; fi # Loop over the file arguments # while [ $file -le $# ]; do # Get the existing XMP data, if any, from the file into a temporary file. # This file will be modified and later put back into the image file. # rdjpgxmp "${!file}" >$TMP1 # Loop over the option arguments # language=x-default mode=overwrite declare -i arg=1 while [ ${!arg} != "--" ]; do case ${!arg} in # --language) let "arg++"; language="${!arg}";; --language) let "arg++"; h="${!arg}"; language=${h:-x-default};; --title) let "arg++"; set-field "$TITLE" "${!arg}" "$language";; --creator) let "arg++"; set-field "$CREATOR" "${!arg}" "$language";; --subject) let "arg++"; set-field "$SUBJECT" "${!arg}" "$language";; --desc*) let "arg++"; set-field "$DESCRIPTION" "${!arg}" "$language";; --publisher) let "arg++"; set-field "$PUBLISHER" "${!arg}" "$language";; --contr*) let "arg++"; set-field "$CONTRIBUTOR" "${!arg}" "$language";; --date) let "arg++"; set-field "$DATE" "${!arg}" x-default;; --identif*) let "arg++"; set-field "$IDENTIFIER" "${!arg}" x-default;; --relation) let "arg++"; set-field "$RELATION" "${!arg}" "$language";; --coverage) let "arg++"; set-field "$COVERAGE" "${!arg}" "$language";; --rights) let "arg++"; set-field "$RIGHTS" "${!arg}" "$language";; --source) let "arg++"; set-field "$SOURCE" "${!arg}" "$language";; --camera) let "arg++"; set-field "$CAMERA" "${!arg}" "$language";; --film) let "arg++"; set-field "$FILM" "${!arg}" "$language";; --lens) let "arg++"; set-field "$LENS" "${!arg}" "$language";; --devel*) let "arg++"; set-field "$DEVEL_DATE" "${!arg}" x-default;; --latitude) let "arg++"; set-lat-or-long "$GPSLatitude" "${!arg}";; --longitude) let "arg++"; set-lat-or-long "$GPSLongitude" "${!arg}";; --altitude) let "arg++"; set-altitude "${!arg}";; --bearingref) let "arg++"; set-bearingref "${!arg}";; --bearing) let "arg++"; set-bearing "${!arg}";; --timezone) let "arg++"; timezone=${!arg};; --from-exif) copy_from_exif "${!file}";; --from-name) let "arg++"; from-name "${!arg}" "${!file}";; --add) mode=add;; --overwrite) mode=overwrite;; --edit) ${EDITOR:-editor} $TMP1;; --list) list-known-fields "$language";; --help) print-help;; *) die "Bug: cannot happen!";; esac let "arg++" done # Add the standard type and format, then write everything back to the image # xmptool -d "$FORMAT" $TMP1 | xmptool -w "$FORMAT" "image/jpeg" |\ xmptool -d "$TYPE" | xmptool -w "$TYPE" "image" |\ xmptool -c | wrjpgxmp "${!file}" >$TMP2 &&\ mv $TMP2 ${!file} &&\ chmod go+r ${!file} let "file++" done 美高梅在线开户