Skip to content

Commit 8734a71

Browse files
authored
Add support for crop_aspect_ratio option (#53)
1 parent aa7403f commit 8734a71

6 files changed

Lines changed: 170 additions & 1 deletion

File tree

src/options/cropAspectRatio.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type {
2+
CropAspectRatio,
3+
CropAspectRatioOptionsPartial,
4+
} from "../types/cropAspectRatio";
5+
import { guardIsUndef, guardIsNotNum, normalizeBoolean } from "../utils";
6+
7+
const getOpt = (
8+
options: CropAspectRatioOptionsPartial
9+
): CropAspectRatio | undefined =>
10+
options.crop_aspect_ratio || options.crop_ar || options.car;
11+
12+
const test = (options: CropAspectRatioOptionsPartial): boolean =>
13+
Boolean(getOpt(options));
14+
15+
const build = (options: CropAspectRatioOptionsPartial): string => {
16+
const cropArOpts = getOpt(options);
17+
18+
guardIsUndef(cropArOpts, "crop_aspect_ratio");
19+
const { aspect_ratio, enlarge } = cropArOpts;
20+
guardIsUndef(aspect_ratio, "crop_aspect_ratio.aspect_ratio");
21+
guardIsNotNum(aspect_ratio, "crop_aspect_ratio.aspect_ratio", {
22+
addParam: { min: 0 },
23+
});
24+
25+
let result = `crop_ar:${aspect_ratio}`;
26+
27+
if (enlarge !== undefined) {
28+
result += `:${normalizeBoolean(enlarge)}`;
29+
}
30+
31+
return result;
32+
};
33+
34+
export { test, build };

src/options/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * as cacheBuster from "../optionsShared/cacheBuster";
1010
export * as colorize from "./colorize";
1111
export * as contrast from "./contrast";
1212
export * as crop from "../optionsShared/crop";
13+
export * as cropAspectRatio from "./cropAspectRatio";
1314
export * as disableAnimation from "./disableAnimation";
1415
export * as duotone from "./duotone";
1516
export * as dpi from "./dpi";

src/types/cropAspectRatio.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* *Crop aspect ratio option*. **PRO feature**
3+
*
4+
* **Description to the generateUrl method**
5+
*
6+
* Defines the aspect ratio to match the crop area. This is a Pro feature.
7+
*
8+
* @param {number} `aspect_ratio` defines the desired aspect ratio for the crop area:
9+
* - When set to `0`, no aspect ratio correction is applied.
10+
* - Any positive number defines the aspect ratio (width/height).
11+
*
12+
* @param `enlarge` {1 | "t" | true} - (optional) controls crop area sizing:
13+
* - When set to `true`, `1`, or `"t"`, imgproxy will enlarge the crop area if needed.
14+
* - If the enlarged crop area exceeds the image size, imgproxy will reduce the area to fit while maintaining the requested aspect ratio.
15+
*
16+
* @see {@link https://docs.imgproxy.net/latest/usage/processing#crop-aspect-ratio | crop aspect ratio imgproxy docs}
17+
*/
18+
interface CropAspectRatio {
19+
aspect_ratio: number;
20+
enlarge?: boolean | 1 | 0 | "t" | "f";
21+
}
22+
23+
/**
24+
* *Crop aspect ratio option*
25+
*
26+
* To describe the crop aspect ratio option, you can use the keyword `crop_aspect_ratio`, `crop_ar`, or `car`.
27+
*
28+
* @see https://docs.imgproxy.net/latest/usage/processing#crop-aspect-ratio
29+
*/
30+
interface CropAspectRatioOptionsPartial {
31+
crop_aspect_ratio?: CropAspectRatio;
32+
crop_ar?: CropAspectRatio;
33+
car?: CropAspectRatio;
34+
}
35+
36+
export { CropAspectRatio, CropAspectRatioOptionsPartial };

src/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { CacheBusterOptionsPartial } from "../typesShared/cacheBuster";
1010
import type { ColorizeOptionsPartial } from "./colorize";
1111
import type { ContrastOptionsPartial } from "./contrast";
1212
import type { CropOptionsPartial } from "../typesShared/crop";
13+
import type { CropAspectRatioOptionsPartial } from "./cropAspectRatio";
1314
import type { DisableAnimationOptionsPartial } from "./disableAnimation";
1415
import type { DuotoneOptionsPartial } from "./duotone";
1516
import type { DPIOptionsPartial } from "./dpi";
@@ -86,6 +87,7 @@ export type Options = AdjustOptionsPartial &
8687
ColorizeOptionsPartial &
8788
ContrastOptionsPartial &
8889
CropOptionsPartial &
90+
CropAspectRatioOptionsPartial &
8991
DisableAnimationOptionsPartial &
9092
DuotoneOptionsPartial &
9193
DPIOptionsPartial &

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const normalizeBoolean = (value: 1 | string | boolean): string => {
1+
export const normalizeBoolean = (value: 1 | 0 | string | boolean): string => {
22
if (value === true || value === "t" || value === 1) {
33
return "t";
44
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { describe, expect, it } from "vitest";
2+
import { test, build } from "../../src/options/cropAspectRatio";
3+
4+
describe("cropAspectRatio", () => {
5+
describe("test", () => {
6+
it("should return true if crop_aspect_ratio option is defined", () => {
7+
expect(test({ crop_aspect_ratio: { aspect_ratio: 1.5 } })).toEqual(true);
8+
});
9+
10+
it("should return true if crop_ar option is defined", () => {
11+
expect(test({ crop_ar: { aspect_ratio: 1.1 } })).toEqual(true);
12+
});
13+
14+
it("should return true if car option is defined", () => {
15+
expect(test({ car: { aspect_ratio: 1.1 } })).toEqual(true);
16+
});
17+
18+
it("should return false if crop_aspect_ratio option is undefined", () => {
19+
expect(test({})).toEqual(false);
20+
});
21+
});
22+
23+
describe("build", () => {
24+
it("should throw an error if crop_aspect_ratio option is undefined", () => {
25+
expect(() => build({})).toThrow("crop_aspect_ratio option is undefined");
26+
});
27+
28+
it("should throw an error if aspect_ratio option is undefined", () => {
29+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
30+
expect(() => build({ crop_aspect_ratio: {} })).toThrow(
31+
"crop_aspect_ratio.aspect_ratio is undefined"
32+
);
33+
});
34+
35+
it("should throw an error if aspect_ratio is not a number", () => {
36+
expect(() =>
37+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
38+
build({ crop_aspect_ratio: { aspect_ratio: "1.5" } })
39+
).toThrow("crop_aspect_ratio.aspect_ratio is not a number");
40+
});
41+
42+
it("should throw an error if aspect_ratio is less than 0", () => {
43+
expect(() =>
44+
build({ crop_aspect_ratio: { aspect_ratio: -1 } })
45+
).toThrowError(
46+
"crop_aspect_ratio.aspect_ratio value can't be less then 0"
47+
);
48+
});
49+
50+
it("should return crop_ar:1.5 if crop_aspect_ratio aspect_ratio is 1.5", () => {
51+
expect(build({ crop_aspect_ratio: { aspect_ratio: 1.5 } })).toEqual(
52+
"crop_ar:1.5"
53+
);
54+
});
55+
56+
it("should return crop_ar:1.1 if crop_ar aspect_ratio is 1.1", () => {
57+
expect(build({ crop_ar: { aspect_ratio: 1.1 } })).toEqual("crop_ar:1.1");
58+
});
59+
60+
it("should return crop_ar:0 if aspect_ratio is 0", () => {
61+
expect(build({ crop_aspect_ratio: { aspect_ratio: 0 } })).toEqual(
62+
"crop_ar:0"
63+
);
64+
});
65+
66+
it("should return crop_ar:1.5:t if crop_aspect_ratio aspect_ratio is 1.5 and enlarge is true", () => {
67+
expect(
68+
build({ crop_aspect_ratio: { aspect_ratio: 1.5, enlarge: true } })
69+
).toEqual("crop_ar:1.5:t");
70+
});
71+
72+
it("should return crop_ar:1.1:t if crop_ar aspect_ratio is 1.1 and enlarge is 1", () => {
73+
expect(build({ crop_ar: { aspect_ratio: 1.1, enlarge: 1 } })).toEqual(
74+
"crop_ar:1.1:t"
75+
);
76+
});
77+
78+
it("should return crop_ar:2:t if aspect_ratio is 2.0 and enlarge is 't'", () => {
79+
expect(
80+
build({ crop_aspect_ratio: { aspect_ratio: 2.0, enlarge: "t" } })
81+
).toEqual("crop_ar:2:t");
82+
});
83+
84+
it("should return crop_ar:1.5:f if aspect_ratio is 1.5 and enlarge is false", () => {
85+
expect(
86+
build({ crop_aspect_ratio: { aspect_ratio: 1.5, enlarge: false } })
87+
).toEqual("crop_ar:1.5:f");
88+
});
89+
90+
it("should return crop_ar:1.1:f if aspect_ratio is 1.1 and enlarge is 'f'", () => {
91+
expect(build({ crop_ar: { aspect_ratio: 1.1, enlarge: "f" } })).toEqual(
92+
"crop_ar:1.1:f"
93+
);
94+
});
95+
});
96+
});

0 commit comments

Comments
 (0)