Skip to content

Instantly share code, notes, and snippets.

@stephenhandley
Created July 24, 2019 00:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stephenhandley/e88e3db5d3c4921747e4a24eb4cd1831 to your computer and use it in GitHub Desktop.
Save stephenhandley/e88e3db5d3c4921747e4a24eb4cd1831 to your computer and use it in GitHub Desktop.
Scrub Exif demo
<html>
<head>
<title>EXIF Scrub</title>
<script src="https://cdn.jsdelivr.net/npm/piexifjs@1.0.6/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>
@stephenhandley
Copy link
Author

Screen Shot 2019-07-23 at 5 15 43 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment