Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/exif-canonical-names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@imgproxy/imgproxy-js-core": minor
---

Add support for the `canonical_names` parameter of the `exif` image-info option. The `exif` option now also accepts an object `{ enabled, canonical_names }`; when `canonical_names` is `1`, `"t"`, or `true`, imgproxy returns EXIF field names in a canonical form (e.g. `DateTimeOriginal`) instead of the human-readable form. The existing boolean-style input is still supported.
19 changes: 18 additions & 1 deletion src/optionsImageInfo/exif.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,27 @@ const getOpt = (options: ExifImageInfoOptionsPartial): Exif | undefined => {
const test = (options: ExifImageInfoOptionsPartial): boolean =>
getOpt(options) !== undefined;

const isExifObject = (
value: Exclude<Exif, undefined>
): value is Exclude<Exif, 1 | 0 | "t" | "f" | boolean | string> =>
typeof value === "object" && value !== null;

const build = (options: ExifImageInfoOptionsPartial): string => {
const exifOpts = getOpt(options);
guardIsUndef(exifOpts, "EXIF");
return `exif:${normalizeBoolean(exifOpts)}`;

if (!isExifObject(exifOpts)) {
return `exif:${normalizeBoolean(exifOpts)}`;
}

const { enabled, canonical_names } = exifOpts;
const enabledPart = enabled !== undefined ? normalizeBoolean(enabled) : "t";

if (canonical_names === undefined) {
return `exif:${enabledPart}`;
}

return `exif:${enabledPart}:${normalizeBoolean(canonical_names)}`;
};

export { test, build };
41 changes: 35 additions & 6 deletions src/typesImageInfo/exif.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
/**
* Boolean-like values for EXIF option fields.
*
* @note Only `1`, `"t"`, or `true` are recognized as truthy values.
* Any other value (including `"true"` or `"1"` as strings) will be treated as false.
*/
type ExifBooleanValue = 1 | 0 | "t" | "f" | boolean | string;

/**
* *EXIF option (object form)*
*
* @param {ExifBooleanValue} enabled - When set to `1`, `"t"` or `true`,
* imgproxy will return the image’s EXIF metadata. Default: `true`.
* @param {ExifBooleanValue} canonical_names - When set to `1`, `"t"` or `true`,
* imgproxy will return the EXIF metadata field names in a canonical form
* (e.g. `DateTimeOriginal`) instead of a human-readable form
* (e.g. `Date and Time (Original)`). Default: `false`.
*/
interface ExifObject {
enabled?: ExifBooleanValue;
canonical_names?: ExifBooleanValue;
}

/**
* *EXIF option*
*
* When set to `1`, `"t"` or `true`, imgproxy will return the image’s EXIF metadata.
* When `enabled` is set to `1`, `"t"` or `true`, imgproxy will return the image’s EXIF metadata.
* When `canonical_names` is set to `1`, `"t"` or `true`, imgproxy will return the EXIF
* metadata field names in a canonical form (e.g. `DateTimeOriginal`) instead of a
* human-readable form (e.g. `Date and Time (Original)`).
*
* Accepts a plain boolean-like value (controls `enabled` only) or an object
* `{ enabled, canonical_names }`.
*
* @note If any value other than `1`, `"t"`, or `true` is passed, it will be recognized as `false`.
*
Expand All @@ -16,22 +45,22 @@
* }
* }
*
* @default true
* @default true:false
*
*
* @see {@link https://docs.imgproxy.net/getting_the_image_info?id=exif | EXIF imgproxy docs}
* @see {@link https://docs.imgproxy.net/usage/getting_info#exif | EXIF imgproxy docs}
*/
type Exif = 1 | "t" | true | false | string;
type Exif = ExifBooleanValue | ExifObject;

/**
* *EXIF option*
*
* To describe the EXIF option, you can use the keyword `exif`.
*
* @see https://docs.imgproxy.net/getting_the_image_info?id=exif
* @see https://docs.imgproxy.net/usage/getting_info#exif
*/
interface ExifImageInfoOptionsPartial {
exif?: Exif;
}

export { Exif, ExifImageInfoOptionsPartial };
export { Exif, ExifObject, ExifBooleanValue, ExifImageInfoOptionsPartial };
39 changes: 38 additions & 1 deletion tests/optionsImageInfo/exif.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ describe("EXIF", () => {
expect(test({ exif: false })).toEqual(true);
});

it("should return true if EXIF option is an object", () => {
expect(test({ exif: { enabled: true } })).toEqual(true);
});

it("should return false if EXIF option is undefined", () => {
expect(test({})).toEqual(false);
});
Expand Down Expand Up @@ -38,12 +42,45 @@ describe("EXIF", () => {
});

it("should return 'f' if EXIF option is 0", () => {
// @ts-expect-error: Let's ignore an error.
expect(build({ exif: 0 })).toEqual("exif:f");
});

it("should return 'f' if EXIF option is string (except 't')", () => {
expect(build({ exif: "true" })).toEqual("exif:f");
});

it("should return 'exif:t' if EXIF option is an object with enabled=true", () => {
expect(build({ exif: { enabled: true } })).toEqual("exif:t");
});

it("should return 'exif:f' if EXIF option is an object with enabled=false", () => {
expect(build({ exif: { enabled: false } })).toEqual("exif:f");
});

it("should default enabled to 't' if only canonical_names is passed", () => {
expect(build({ exif: { canonical_names: true } })).toEqual("exif:t:t");
});

it("should return 'exif:t:t' if both fields are true", () => {
expect(build({ exif: { enabled: true, canonical_names: true } })).toEqual(
"exif:t:t"
);
});

it("should return 'exif:f:t' if enabled=false and canonical_names=true", () => {
expect(
build({ exif: { enabled: false, canonical_names: true } })
).toEqual("exif:f:t");
});

it("should return 'exif:t:f' if enabled=true and canonical_names=false", () => {
expect(
build({ exif: { enabled: true, canonical_names: false } })
).toEqual("exif:t:f");
});

it("should return 'exif:t' for an empty object", () => {
expect(build({ exif: {} })).toEqual("exif:t");
});
});
});
Loading