Skip to content

Commit

Permalink
Add more parsing prefilters to ExifDate and more tests to Exif* parsers.
Browse files Browse the repository at this point in the history
  • Loading branch information
mceachen committed Sep 8, 2023
1 parent 55b910e commit cf1d59b
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 54 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ vendored versions of ExifTool match the version that they vendor.

## Version history

### v22.2.3

- 🐞 Apply the v22.2.3 bugfix _even wider_ (just found a `SubSecTime` value of "01" in the wild, and it was happily parsed into today's date, oops).

### v22.2.2

- 🐞 Apply the v22.2.1 bugfix wider: we now reject parsing any date-ish or time-ish value that matches `/^0+$/`.
Expand Down
38 changes: 19 additions & 19 deletions docs/classes/ExifDate.html

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions docs/classes/ExifTime.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h4>Hierarchy</h4>
<ul class="tsd-hierarchy">
<li><span class="target">ExifTime</span></li></ul></section><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L8">src/ExifTime.ts:8</a></li></ul></aside>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L9">src/ExifTime.ts:9</a></li></ul></aside>
<section class="tsd-panel-group tsd-index-group">
<section class="tsd-panel tsd-index-panel">
<details class="tsd-index-content tsd-index-accordion" open><summary class="tsd-accordion-summary tsd-index-summary">
Expand Down Expand Up @@ -78,34 +78,34 @@ <h5><code class="tsd-tag ts-flagOptional">Optional</code> <span class="tsd-kind-
<h5><code class="tsd-tag ts-flagOptional">Optional</code> <span class="tsd-kind-parameter">rawValue</span>: <span class="tsd-signature-type">string</span></h5></li></ul></div>
<h4 class="tsd-returns-title">Returns <a href="ExifTime.html" class="tsd-signature-type tsd-kind-class">ExifTime</a></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L25">src/ExifTime.ts:25</a></li></ul></aside></li></ul></section></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L31">src/ExifTime.ts:31</a></li></ul></aside></li></ul></section></section>
<section class="tsd-panel-group tsd-member-group">
<h2>Properties</h2>
<section class="tsd-panel tsd-member"><a id="hour" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagReadonly">Readonly</code> <span>hour</span><a href="#hour" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<div class="tsd-signature"><span class="tsd-kind-property">hour</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L26">src/ExifTime.ts:26</a></li></ul></aside></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L32">src/ExifTime.ts:32</a></li></ul></aside></section>
<section class="tsd-panel tsd-member"><a id="millisecond" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagOptional">Optional</code> <code class="tsd-tag ts-flagReadonly">Readonly</code> <span>millisecond</span><a href="#millisecond" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<div class="tsd-signature"><span class="tsd-kind-property">millisecond</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L29">src/ExifTime.ts:29</a></li></ul></aside></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L35">src/ExifTime.ts:35</a></li></ul></aside></section>
<section class="tsd-panel tsd-member"><a id="minute" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagReadonly">Readonly</code> <span>minute</span><a href="#minute" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<div class="tsd-signature"><span class="tsd-kind-property">minute</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L27">src/ExifTime.ts:27</a></li></ul></aside></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L33">src/ExifTime.ts:33</a></li></ul></aside></section>
<section class="tsd-panel tsd-member"><a id="rawValue" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagOptional">Optional</code> <code class="tsd-tag ts-flagReadonly">Readonly</code> <span>raw<wbr/>Value</span><a href="#rawValue" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<div class="tsd-signature"><span class="tsd-kind-property">raw<wbr/>Value</span><span class="tsd-signature-symbol">?:</span> <span class="tsd-signature-type">string</span></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L30">src/ExifTime.ts:30</a></li></ul></aside></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L36">src/ExifTime.ts:36</a></li></ul></aside></section>
<section class="tsd-panel tsd-member"><a id="second" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagReadonly">Readonly</code> <span>second</span><a href="#second" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<div class="tsd-signature"><span class="tsd-kind-property">second</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">number</span></div><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L28">src/ExifTime.ts:28</a></li></ul></aside></section></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L34">src/ExifTime.ts:34</a></li></ul></aside></section></section>
<section class="tsd-panel-group tsd-member-group">
<h2>Accessors</h2>
<section class="tsd-panel tsd-member"><a id="millis" class="tsd-anchor"></a>
Expand All @@ -115,7 +115,7 @@ <h3 class="tsd-anchor-link"><span>millis</span><a href="#millis" aria-label="Per
<li class="tsd-description">
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">undefined</span><span class="tsd-signature-symbol"> | </span><span class="tsd-signature-type">number</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L33">src/ExifTime.ts:33</a></li></ul></aside></li></ul></section></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L39">src/ExifTime.ts:39</a></li></ul></aside></li></ul></section></section>
<section class="tsd-panel-group tsd-member-group">
<h2>Methods</h2>
<section class="tsd-panel tsd-member"><a id="toExifString" class="tsd-anchor"></a>
Expand All @@ -125,15 +125,15 @@ <h3 class="tsd-anchor-link"><span>to<wbr/>Exif<wbr/>String</span><a href="#toExi
<li class="tsd-description">
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">string</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L51">src/ExifTime.ts:51</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L57">src/ExifTime.ts:57</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="toISOString" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>toISOString</span><a href="#toISOString" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
<li class="tsd-signature tsd-anchor-link" id="toISOString.toISOString-1"><span class="tsd-kind-call-signature">toISOString</span><span class="tsd-signature-symbol">(</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">string</span><a href="#toISOString.toISOString-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></li>
<li class="tsd-description">
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">string</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L47">src/ExifTime.ts:47</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L53">src/ExifTime.ts:53</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="toJSON" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>toJSON</span><a href="#toJSON" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -154,15 +154,15 @@ <h5><span class="tsd-kind-property">raw<wbr/>Value</span><span class="tsd-signat
<li class="tsd-parameter">
<h5><span class="tsd-kind-property">second</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">number</span></h5></li></ul><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L55">src/ExifTime.ts:55</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L61">src/ExifTime.ts:61</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="toString" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><span>to<wbr/>String</span><a href="#toString" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
<li class="tsd-signature tsd-anchor-link" id="toString.toString-1"><span class="tsd-kind-call-signature">to<wbr/>String</span><span class="tsd-signature-symbol">(</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">string</span><a href="#toString.toString-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></li>
<li class="tsd-description">
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">string</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L43">src/ExifTime.ts:43</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L49">src/ExifTime.ts:49</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="fromDateTime" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>from<wbr/>Date<wbr/>Time</span><a href="#fromDateTime" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -177,7 +177,7 @@ <h5><span class="tsd-kind-parameter">dt</span>: <span class="tsd-signature-type
<h5><code class="tsd-tag ts-flagOptional">Optional</code> <span class="tsd-kind-parameter">rawValue</span>: <span class="tsd-signature-type">string</span></h5></li></ul></div>
<h4 class="tsd-returns-title">Returns <a href="../types/Maybe.html" class="tsd-signature-type tsd-kind-type-alias">Maybe</a><span class="tsd-signature-symbol">&lt;</span><a href="ExifTime.html" class="tsd-signature-type tsd-kind-class">ExifTime</a><span class="tsd-signature-symbol">&gt;</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L19">src/ExifTime.ts:19</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L25">src/ExifTime.ts:25</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="fromEXIF" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>fromEXIF</span><a href="#fromEXIF" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -190,7 +190,7 @@ <h4 class="tsd-parameters-title">Parameters</h4>
<h5><span class="tsd-kind-parameter">text</span>: <span class="tsd-signature-type">string</span></h5></li></ul></div>
<h4 class="tsd-returns-title">Returns <a href="../types/Maybe.html" class="tsd-signature-type tsd-kind-type-alias">Maybe</a><span class="tsd-signature-symbol">&lt;</span><a href="ExifTime.html" class="tsd-signature-type tsd-kind-class">ExifTime</a><span class="tsd-signature-symbol">&gt;</span></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L9">src/ExifTime.ts:9</a></li></ul></aside></li></ul></section>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L10">src/ExifTime.ts:10</a></li></ul></aside></li></ul></section>
<section class="tsd-panel tsd-member"><a id="fromJSON" class="tsd-anchor"></a>
<h3 class="tsd-anchor-link"><code class="tsd-tag ts-flagStatic">Static</code> <span>fromJSON</span><a href="#fromJSON" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="#icon-anchor"></use></svg></a></h3>
<ul class="tsd-signatures">
Expand All @@ -216,7 +216,7 @@ <h5><span class="tsd-kind-property">raw<wbr/>Value</span><span class="tsd-signat
<h5><span class="tsd-kind-property">second</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">number</span></h5></li></ul></li></ul></div>
<h4 class="tsd-returns-title">Returns <a href="ExifTime.html" class="tsd-signature-type tsd-kind-class">ExifTime</a></h4><aside class="tsd-sources">
<ul>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L66">src/ExifTime.ts:66</a></li></ul></aside></li></ul></section></section></div>
<li>Defined in <a href="https://github.com/photostructure/exiftool-vendored.js/blob/main/src/ExifTime.ts#L72">src/ExifTime.ts:72</a></li></ul></aside></li></ul></section></section></div>
<div class="col-sidebar">
<div class="page-menu">
<div class="tsd-navigation settings">
Expand Down
2 changes: 1 addition & 1 deletion docs/classes/ExifTool.html
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ <h3 class="tsd-anchor-link"><span>rewrite<wbr/>All<wbr/>Tags</span><a href="#rew
write to it without errors, but some metadata from the original image may
be lost in the process.</p>
<p>This should only be applied as a last resort to images whose metadata is
not readable via .read().</p>
not readable via <a href="ExifTool.html#read" class="tsd-kind-method">()</a>.</p>
</div>
<div class="tsd-parameters">
<h4 class="tsd-parameters-title">Parameters</h4>
Expand Down
3 changes: 2 additions & 1 deletion src/ExifDate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ describe("ExifDate", () => {
)
})
}
for (const ea of ["", " ", "0000", "1958", "2010_08"]) {
for (const ea of ["", " ", "0", "00", "01", "0000", "1958", "2010_08"]) {
it(`rejects "${ea}"`, () => {
expect(ExifDate.fromEXIF(ea)).to.eql(undefined)
expect(ExifDate.from(ea)).to.eql(undefined)
})
}
})
29 changes: 22 additions & 7 deletions src/ExifDate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { DateTime } from "luxon"

import { HourMs, validDateTime } from "./DateTime"
import { Maybe, first, firstDefinedThunk, map } from "./Maybe"
import { Maybe, firstDefinedThunk } from "./Maybe"
import { blank, pad2, toS } from "./String"

const StrictExifRE = /^\d+:\d+:\d+|\d+-\d+-\d+$/
const LooseExifRE = /^\S+\s+\S+\s+\S+$/

/**
* Encodes an ExifDate
*/
Expand All @@ -17,15 +20,21 @@ export class ExifDate {
)
}
static fromISO(text: string): Maybe<ExifDate> {
return this.fromDateTime(DateTime.fromISO(text), text)
return StrictExifRE.test(toS(text).trim())
? this.fromDateTime(DateTime.fromISO(text), text)
: undefined
}

private static fromPatterns(text: string, fmts: string[]) {
if (blank(text)) return
text = toS(text).trim()
return first(fmts, (fmt) =>
map(DateTime.fromFormat(text, fmt), (dt) => this.fromDateTime(dt, text))
)
for (const fmt of fmts) {
const dt = DateTime.fromFormat(text, fmt)
if (validDateTime(dt)) {
return this.fromDateTime(dt, text)
}
}
return
}

// These are all formats I've seen in the wild from exiftool's output.
Expand All @@ -35,11 +44,17 @@ export class ExifDate {
// chance of misinterpreting a given value.

static fromExifStrict(text: string): Maybe<ExifDate> {
return this.fromPatterns(text, ["y:MM:dd", "y-MM-dd", "y:M:d"])
return StrictExifRE.test(toS(text).trim())
? this.fromPatterns(text, ["y:MM:dd", "y-MM-dd", "y:M:d"])
: undefined
}

static fromExifLoose(text: string): Maybe<ExifDate> {
return this.fromPatterns(text, ["MMM d y", "MMMM d y"])
// Unfortunately, Luxon parses "00" and "01" as _today_. So if we don't
// three non-blank strings parts, reject.
return LooseExifRE.test(toS(text).trim())
? this.fromPatterns(text, ["MMM d y", "MMMM d y"])
: undefined
}

static fromEXIF(text: string): Maybe<ExifDate> {
Expand Down
14 changes: 13 additions & 1 deletion src/ExifTime.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,19 @@ describe("ExifTime", () => {
})

describe("rejects invalid raw values", () => {
for (const ea of [null, undefined, "", " ", "0", "00", "a"]) {
for (const ea of [
null,
undefined,
"",
" ",
"0",
"00",
"01",
"02",
"0001",
"1958",
"a",
]) {
it(`rejects ${JSON.stringify(ea)}`, () => {
expect(ExifTime.fromEXIF(ea as any)).to.eql(undefined)
})
Expand Down
24 changes: 15 additions & 9 deletions src/ExifTime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DateTime } from "luxon"
import { first, map, Maybe } from "./Maybe"
import { validDateTime } from "./DateTime"
import { Maybe } from "./Maybe"
import { blank, pad2, pad3, toS } from "./String"

/**
Expand All @@ -9,17 +10,22 @@ export class ExifTime {
static fromEXIF(text: string): Maybe<ExifTime> {
text = toS(text).trim()
if (blank(text)) return
return first(
["HH:mm:ss.uZZ", "HH:mm:ssZZ", "HH:mm:ss.u", "HH:mm:ss"],
(fmt) =>
map(DateTime.fromFormat(text, fmt), (dt) => this.fromDateTime(dt, text))
)
for (const fmt of [
"HH:mm:ss.uZZ",
"HH:mm:ssZZ",
"HH:mm:ss.u",
"HH:mm:ss",
]) {
const result = this.fromDateTime(DateTime.fromFormat(text, fmt), text)
if (result != null) return result
}
return
}

static fromDateTime(dt: DateTime, rawValue?: string): Maybe<ExifTime> {
return dt == null || !dt.isValid
? undefined
: new ExifTime(dt.hour, dt.minute, dt.second, dt.millisecond, rawValue)
return validDateTime(dt)
? new ExifTime(dt.hour, dt.minute, dt.second, dt.millisecond, rawValue)
: undefined
}

constructor(
Expand Down
2 changes: 1 addition & 1 deletion src/ExifTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ export class ExifTool {
* be lost in the process.
*
* This should only be applied as a last resort to images whose metadata is
* not readable via {@link .read()}.
* not readable via {@link ExifTool.read()}.
*
* @see https://exiftool.org/faq.html#Q20
*
Expand Down

0 comments on commit cf1d59b

Please sign in to comment.