Created
July 24, 2019 00:16
-
-
Save stephenhandley/e88e3db5d3c4921747e4a24eb4cd1831 to your computer and use it in GitHub Desktop.
Scrub Exif demo
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
<html> | |
<head> | |
<title>EXIF Scrub</title> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/piexif.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script> | |
<script> | |
function onFileChosen (file) { | |
const reader = new FileReader(); | |
reader.readAsDataURL(file); | |
reader.onloadend = function (event) { | |
const src = event.target.result; | |
let exif = piexif.load(src); | |
renderImage({ | |
id: 'Original', | |
src, | |
exif | |
}); | |
const pathsInput = document.getElementById('Paths'); | |
const paths = pathsInput.value.split(/,\s*/); | |
let scrubbedExif = scrubExif({exif, paths}); | |
const scrubbedExifBytes = piexif.dump(scrubbedExif); | |
const scrubbedSrc = piexif.insert(scrubbedExifBytes, src); | |
scrubbedExif = piexif.load(scrubbedSrc); | |
renderImage({ | |
id: 'Scrubbed', | |
src: scrubbedSrc, | |
exif: scrubbedExif | |
}); | |
}; | |
} | |
function renderImage ({id, src, exif}) { | |
const container = document.getElementById(id); | |
const header = document.createElement('h1'); | |
const headerText = document.createTextNode(id); | |
header.appendChild(headerText); | |
container.appendChild(header); | |
const img = document.createElement('img'); | |
img.src = src; | |
container.appendChild(img); | |
const exifNode = document.createElement('pre'); | |
const json = JSON.stringify(prettyExif(exif), null, 1); | |
const jsonText = document.createTextNode(json); | |
exifNode.appendChild(jsonText); | |
container.appendChild(exifNode); | |
} | |
function prettyExif (exif) { | |
return _.mapValues(exif, (tags, ifd)=> { | |
if (ifd === 'thumbnail') { | |
return tags; | |
} | |
return _.mapKeys(tags, (value, tag)=> { | |
return piexif.TAGS[ifd][tag].name; | |
}); | |
}); | |
} | |
function scrubExif ({exif, paths}) { | |
const tagIdsByIfd = {}; | |
const ifds = []; | |
for (const path of paths) { | |
const [ifd, tag] = path.split('.'); | |
if (tag) { | |
const tagId = findTagId({ifd, tag}); | |
if (!tagId) { | |
console.error(`Skipping invalid path: ${path}`); | |
continue; | |
} | |
if (ifd in tagIdsByIfd) { | |
tagIdsByIfd[ifd].push(tagId) | |
} else { | |
tagIdsByIfd[ifd] = [tagId]; | |
} | |
} else { | |
ifds.push(ifd); | |
} | |
} | |
// 1. Remove the top level fields like "GPS" | |
scrubbed = _.clone(exif); | |
for (const ifd of ifds) { | |
scrubbed[ifd] = {}; | |
} | |
// 2. Remove nested fields like "Exif.ColorSpace" | |
for (const [ifd, tagIds] of Object.entries(tagIdsByIfd)) { | |
scrubbed[ifd] = _.omit(scrubbed[ifd], tagIds); | |
} | |
return scrubbed; | |
} | |
function findTagId ({ifd, tag}) { | |
const tagMap = piexif.TAGS[ifd]; | |
return Object.keys(tagMap).find((tagId)=> { | |
const {name} = tagMap[tagId]; | |
return (name === tag); | |
}); | |
} | |
</script> | |
<style> | |
input[type=text] { | |
width: 400px; | |
display: block; | |
} | |
img, pre { | |
max-width: 400px; | |
} | |
pre { | |
overflow: scroll; | |
} | |
#Original, #Scrubbed { | |
vertical-align: top; | |
display: inline-block; | |
margin-left: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<form id="Chooser"> | |
<div>Paths to scrub</div> | |
<input type="text" id="Paths" value="Exif.ColorSpace, GPS"/> | |
<input type="file" id="File" accept="image/*" onchange="onFileChosen(this.files[0])"> | |
</form> | |
<div id="Original"></div> | |
<div id="Scrubbed"></div> | |
</html> |
Author
stephenhandley
commented
Jul 24, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment