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
6 changes: 6 additions & 0 deletions .changeset/fair-flowers-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@imgproxy/imgproxy-js-core": minor
---

- Add `ch` (chessboard order) position to `watermark` option.
- Add `watermark_rotate` option.
1 change: 1 addition & 0 deletions src/options/watermark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const currentPositions = {
soea: true,
sowe: true,
re: true,
ch: true,
};

const getOpt = (options: WatermarkOptionsPartial): Watermark | undefined =>
Expand Down
24 changes: 24 additions & 0 deletions src/options/watermarkRotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type {
WatermarkRotate,
WatermarkRotateOptionsPartial,
} from "../types/watermarkRotate";
import { guardIsUndef, guardIsNotNum } from "../utils";

const getOpt = (
options: WatermarkRotateOptionsPartial
): WatermarkRotate | undefined =>
options.watermark_rotate ?? options.wm_rot ?? options.wmr;

const test = (options: WatermarkRotateOptionsPartial): boolean =>
getOpt(options) !== undefined;

const build = (options: WatermarkRotateOptionsPartial): string => {
const watermarkRotateOpts = getOpt(options);

guardIsUndef(watermarkRotateOpts, "watermark_rotate");
guardIsNotNum(watermarkRotateOpts, "watermark_rotate");

return `wmr:${watermarkRotateOpts}`;
};

export { test, build };
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import type { TrimOptionsPartial } from "./trim";
import type { UnsharpMaskingOptionsPartial } from "./unsharpMasking";
import type { VideoThumbnailSecondOptionsPartial } from "../typesShared/videoThumbnailSecond";
import type { WatermarkOptionsPartial } from "./watermark";
import type { WatermarkRotateOptionsPartial } from "./watermarkRotate";
import type { WatermarkShadowOptionsPartial } from "./watermarkShadow";
import type { WatermarkSizeOptionsPartial } from "./watermarkSize";
import type { WatermarkTextOptionsPartial } from "./watermarkText";
Expand Down Expand Up @@ -133,6 +134,7 @@ export type Options = AdjustOptionsPartial &
WatermarkSizeOptionsPartial &
WatermarkTextOptionsPartial &
WatermarkUrlOptionsPartial &
WatermarkRotateOptionsPartial &
WebpOptionsPartial &
WidthOptionsPartial &
ZoomOptionsPartial &
Expand Down
10 changes: 6 additions & 4 deletions src/types/watermark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* @param {number} opacity - watermark opacity modifier.
* Final opacity is calculated like general opacity option * opacity watermark. Value range: `0` - `1`.
* @param {"ce" | "no" | "so" | "ea" | "we" | "noea" | "nowe" | "soea" | "sowe" | "re"} [position="ce"] -
* @param {"ce" | "no" | "so" | "ea" | "we" | "noea" | "nowe" | "soea" | "sowe" | "re" | "ch"} [position="ce"] -
* (optional) specifies the position of the watermark. Default `"ce"`.
*
* Available values:
Expand All @@ -17,15 +17,16 @@
* - `soea`: south-east (bottom-right corner)
* - `sowe`: south-west (bottom-left corner)
* - `re`: repeat and tile the watermark to fill the entire image
* - `ch`: **PRO feature** same as `re` but watermarks are placed in a chessboard order
*
* @param {number} [x_offset] - (optional) specifies the horizontal offset for the watermark.
* You can use negative values, this means that the watermark will be shifted towards the edge that is selected.
* That is, shifted by the selected number of pixels beyond the edge.
* When using `re` position, these values define the spacing between the tiles.
* When using `re` or `ch` position, these values define the spacing between the tiles.
* @param {number} [y_offset] - (optional) specifies the vertical offset for the watermark.
* You can use negative values, this means that the watermark will be shifted towards the edge that is selected.
* That is, shifted by the selected number of pixels beyond the edge.
* When using `re` position, these values define the spacing between the tiles.
* When using `re` or `ch` position, these values define the spacing between the tiles.
* @param {number} [scale] - (optional) a floating-point number that defines
* the watermark size relative to the resultant image size.
* When set to 0 or when omitted, the watermark size won’t be changed.
Expand Down Expand Up @@ -65,7 +66,8 @@ interface Watermark {
| "nowe"
| "soea"
| "sowe"
| "re";
| "re"
| "ch";
x_offset?: number;
y_offset?: number;
scale?: number;
Expand Down
26 changes: 26 additions & 0 deletions src/types/watermarkRotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* *Watermark rotate option*. **PRO feature**
*
* Rotates the watermark on the specified angle (clockwise).
* The orientation from the image metadata is applied before the rotation.
*
* @default 0
*
* @see {@link https://docs.imgproxy.net/generating_the_url?id=watermark-rotate | watermark rotate option imgproxy docs}
*/
type WatermarkRotate = number;

/**
* *Watermark rotate*. **PRO feature**
*
* To describe the Watermark rotate option, you can use the keyword `watermark_rotate`, `wm_rot`, or `wmr`.
*
* @see {@link https://docs.imgproxy.net/generating_the_url?id=watermark-rotate | watermark rotate option imgproxy docs}
*/
interface WatermarkRotateOptionsPartial {
watermark_rotate?: WatermarkRotate;
wm_rot?: WatermarkRotate;
wmr?: WatermarkRotate;
}

export { WatermarkRotate, WatermarkRotateOptionsPartial };
8 changes: 7 additions & 1 deletion tests/optionsBasic/watermark.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ describe("watermark", () => {
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
build({ watermark: { opacity: 0.2, position: "top" } })
).toThrow(
"watermark.position is invalid. Valid values are: ce, no, so, ea, we, noea, nowe, soea, sowe, re"
"watermark.position is invalid. Valid values are: ce, no, so, ea, we, noea, nowe, soea, sowe, re, ch"
);
});

it("should accept ch position", () => {
expect(build({ watermark: { opacity: 0.2, position: "ch" } })).toEqual(
"wm:0.2:ch"
);
});

Expand Down
66 changes: 66 additions & 0 deletions tests/optionsBasic/watermarkRotate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { assertType, describe, expect, expectTypeOf, it } from "vitest";
import { test, build } from "../../src/options/watermarkRotate";
import { Options } from "../../src/types";

const NAMES = ["watermark_rotate", "wm_rot", "wmr"] as const;

describe.each(NAMES)("%s", name => {
describe("test", () => {
it("should return true if option is defined", () => {
expect(test({ [name]: 45 })).toEqual(true);
});

it("should return true if option is equal 0", () => {
expect(test({ [name]: 0 })).toEqual(true);
});

it("should return false if option is undefined", () => {
expect(test({})).toEqual(false);
});
});

describe("build", () => {
it("should throw an error if option is undefined", () => {
expect(() => build({})).toThrow("watermark_rotate option is undefined");
});

it("should throw an error if option is not a number", () => {
expect(() => build({ [name]: "150" })).toThrow(
"watermark_rotate option is not a number"
);
});

it("should buld result", () => {
expect(build({ [name]: 45 })).toEqual("wmr:45");
});

it("should correctly handle 0 value", () => {
expect(build({ [name]: 0 })).toEqual("wmr:0");
});
});
});

describe("Check `watermark_rotate` type declarations", () => {
it("watermark_rotate option should have `number` type", () => {
expectTypeOf(build).parameter(0).toEqualTypeOf<{
watermark_rotate?: number;
wm_rot?: number;
wmr?: number;
}>();
expectTypeOf(build).returns.toEqualTypeOf<string>();
});

it("check TS type declaration", () => {
assertType<Options>({
watermark_rotate: 45,
});

assertType<Options>({
wm_rot: 45,
});

assertType<Options>({
wmr: 45,
});
});
});