├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── core
├── .gitignore
├── README.md
├── dist
│ ├── index.cjs.js
│ ├── index.cjs.js.map
│ ├── index.d.ts
│ ├── index.esm.js
│ ├── index.esm.js.map
│ ├── index.html
│ ├── watermark.js
│ ├── watermark.js.map
│ ├── watermark.min.js
│ └── watermark.min.js.map
├── package.json
├── rollup.config.ts
├── src
│ └── index.ts
└── tsconfig.json
├── lerna.json
├── package.json
├── react
├── .kktrc.ts
├── README.md
├── package.json
├── src
│ └── index.tsx
└── tsconfig.json
├── renovate.json
├── test
└── index.test.tsx
├── tsconfig.json
└── website
├── .kktrc.ts
├── README.md
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── index.tsx
└── react-app-env.d.ts
└── tsconfig.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: jaywcjlove
2 | buy_me_a_coffee: jaywcjlove
3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"]
4 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | windows:
9 | runs-on: windows-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - uses: actions/setup-node@v4
13 | with:
14 | node-version: 20
15 |
16 | - name: Look Changelog
17 | uses: jaywcjlove/changelog-generator@main
18 | with:
19 | filter-author: (小弟调调™|Renovate Bot|renovate-bot)
20 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}'
21 |
22 | - run: npm install
23 | - run: npm run build
24 | - run: npm run coverage
25 | - run: npm run doc
26 |
27 | build:
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v4
31 | - uses: actions/setup-node@v4
32 | with:
33 | node-version: 20
34 | registry-url: 'https://registry.npmjs.org'
35 |
36 | - run: npm install
37 | - run: npm run build
38 | - run: npm run coverage
39 | - run: npm run bundle
40 | - run: npm run bundle:min
41 | - run: npm install
42 | - run: npm run doc
43 |
44 | - name: Generate Contributors Images
45 | uses: jaywcjlove/github-action-contributors@main
46 | with:
47 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\])
48 | output: website/build/CONTRIBUTORS.svg
49 | avatarSize: 42
50 |
51 | - name: Create Coverage Badges
52 | uses: jaywcjlove/coverage-badges-cli@main
53 | with:
54 | output: website/build/badges.svg
55 |
56 | - run: cp -rp coverage website/build
57 | - run: cp -rp core/dist/*.js website/build
58 | - run: cp -rp core/dist/*.ts website/build
59 | - run: cp -rp core/dist/*.map website/build
60 | - run: cp core/dist/index.html website/build/example.html
61 |
62 | - name: Create watermark-example.svg
63 | working-directory: website/build
64 | run: |
65 | cat > watermark-example.svg << EOF
66 |
67 | EOF
68 |
69 | - name: Is a tag created auto?
70 | id: create_tag
71 | uses: jaywcjlove/create-tag-action@main
72 | with:
73 | token: ${{ secrets.GITHUB_TOKEN }}
74 | package-path: ./react/package.json
75 |
76 | - name: get tag version
77 | id: tag_version
78 | uses: jaywcjlove/changelog-generator@main
79 |
80 | - name: Deploy
81 | uses: peaceiris/actions-gh-pages@v4
82 | with:
83 | commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }}
84 | github_token: ${{ secrets.GITHUB_TOKEN }}
85 | publish_dir: ./website/build
86 |
87 | - name: Generate Changelog
88 | id: changelog
89 | uses: jaywcjlove/changelog-generator@main
90 | if: steps.create_tag.outputs.successful
91 | with:
92 | head-ref: ${{ steps.create_tag.outputs.version }}
93 | filter-author: (小弟调调™|Renovate Bot|renovate-bot)
94 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}'
95 |
96 | - name: Create Release
97 | uses: ncipollo/release-action@v1
98 | if: steps.create_tag.outputs.successful
99 | with:
100 | allowUpdates: true
101 | token: ${{ secrets.GITHUB_TOKEN }}
102 | name: ${{ steps.changelog.outputs.tag }}
103 | tag: ${{ steps.changelog.outputs.tag }}
104 | body: |
105 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/uiwjs/react-watermark/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html
106 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }}
107 |
108 | ${{ steps.changelog.outputs.changelog }}
109 |
110 | # - run: git status
111 | # - run: npm install @jsdevtools/npm-publish -g
112 | # - run: npm-publish --token="${{ secrets.NPM_TOKEN }}" ./react/package.json
113 |
114 |
115 | - run: npm publish --access public
116 | name: 📦 @uiw/react-watermark to NPM
117 | working-directory: react
118 | continue-on-error: true
119 | env:
120 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
121 |
122 | - run: npm publish --access public
123 | name: 📦 @uiw/watermark.js to NPM
124 | working-directory: core
125 | continue-on-error: true
126 | env:
127 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
128 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 | build
5 | lib
6 | esm
7 | cjs
8 |
9 | dist.css
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn.lock
14 | yarn-debug.log*
15 | yarn-error.log*
16 | pnpm-debug.log*
17 | package-lock.json
18 |
19 | # local env files
20 | .env.local
21 | .env.*.local
22 |
23 | # Editor directories and files
24 | .DS_Store
25 | .idea
26 | .lerna_backup
27 | .vscode
28 | *.suo
29 | *.ntvs*
30 | *.njsproj
31 | *.sln
32 | *.sw?
33 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.yml
5 | package.json
6 | node_modules
7 | dist
8 | build
9 | lib
10 | esm
11 | cjs
12 | test
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 120,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 uiw
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | react/README.md
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | !dist
--------------------------------------------------------------------------------
/core/README.md:
--------------------------------------------------------------------------------
1 | Watermark.js
2 | ===
3 |
4 | [](https://jaywcjlove.github.io/#/sponsor)
5 | [](https://github.com/uiwjs/react-watermark/actions/workflows/ci.yml)
6 | [](https://uiwjs.github.io/react-watermark/coverage/lcov-report/)
7 | [](https://www.npmjs.com/package/@uiw/watermark.js)
8 | [](https://www.npmjs.com/package/@uiw/watermark.js)
9 |
10 | JavaScript library for generating image watermarks using canvas. support [`react`](https://www.npmjs.com/package/@uiw/react-watermark).
11 |
12 | ## Install
13 |
14 | ```bash
15 | npm i @uiw/watermark.js
16 | # Or
17 | npm i @uiw/react-watermark
18 | ```
19 |
20 | ## Using
21 |
22 | ```js
23 | import Watermark from '@uiw/watermark.js';
24 |
25 | const watermark = new Watermark({
26 | content: 'Hello Watermark!'
27 | });
28 | watermark.create().then((mark) => {
29 | console.log('output:', mark)
30 | })
31 | .catch((err) => {
32 | console.log(err, 'err')
33 | })
34 | ```
35 |
36 | Or manually download and link `watermark.js` in your HTML, It can also be downloaded via [UNPKG](https://unpkg.com/browse/@uiw/watermark.js/):
37 |
38 | CDN: [UNPKG](https://unpkg.com/@uiw/watermark.js/dist/) | [jsDelivr](https://cdn.jsdelivr.net/npm/@uiw/watermark.js/) | [Githack](https://raw.githack.com/uiwjs/watermark.js/gh-pages/watermark.min.js) | [Statically](https://cdn.statically.io/gh/uiwjs/watermark.js/gh-pages/watermark.min.js)
39 |
40 | ```html
41 |
54 |
55 |
56 |
57 |
69 |
70 | ```
71 |
72 | ## Used in React
73 |
74 | ```jsx
75 | import React from "react";
76 | import Watermark from '@uiw/react-watermark';
77 |
78 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
79 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
80 |
81 | Declarative views make your code more predictable and easier to debug.`;
82 |
83 | export default function App() {
84 | return (
85 |
89 |
90 |
91 | );
92 | }
93 | ```
94 |
95 | ## API
96 |
97 | ```ts
98 | export interface WatermarkOptions {
99 | /** watermark text content */
100 | content?: string | string[];
101 | /**
102 | * When the watermark is drawn, the rotation angle, in `°`. @default `-22`
103 | */
104 | rotate?: number;
105 | /**
106 | * High-definition print image source, for high-definition screen display,
107 | * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.
108 | */
109 | image?: string;
110 | /** Horizontal spacing between watermarks. @default `212` */
111 | gapX?: number;
112 | /** vertical spacing between watermarks. @default `222` */
113 | gapY?: number;
114 | /** width of watermark. @default `120` */
115 | width?: number;
116 | /** height of watermark @default `64` */
117 | height?: number;
118 | /**
119 | * The vertical offset of the watermark drawn on the canvas.
120 | * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`
121 | */
122 | offsetLeft?: number;
123 | /**
124 | * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,
125 | * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`
126 | */
127 | offsetTop?: number;
128 | /** text size @default `16` */
129 | fontSize?: number;
130 | /** text family @default `sans-serif` */
131 | fontFamily?: string;
132 | /** text weight @default `normal` */
133 | fontWeight?: 'normal' | 'light' | 'weight' | number;
134 | /** text color @default `rgba(0,0,0,.15)` */
135 | fontColor?: string;
136 | /** text style */
137 | fontStyle?: CanvasFillStrokeStyles['fillStyle'];
138 | }
139 | export default class Watermark {
140 | option: WatermarkOptions;
141 | constructor(options: WatermarkOptions);
142 | create(): Promise;
143 | }
144 | ```
145 |
146 | ## Contributors
147 |
148 | As always, thanks to our amazing contributors!
149 |
150 |
151 |
152 |
153 |
154 | Made with [action-contributors](https://github.com/uiwjs/github-action-contributors).
155 |
156 | ## License
157 |
158 | Licensed under the [MIT License](https://opensource.org/licenses/MIT).
--------------------------------------------------------------------------------
/core/dist/index.cjs.js:
--------------------------------------------------------------------------------
1 | /*! @uiw/watermark.js v1.0.1 | MIT © 2024 kenny wang https://uiwjs.github.io/react-watermark */
2 | 'use strict';
3 |
4 | /**
5 | * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution
6 | * @param context
7 | * @returns
8 | */
9 | const getPixelRatio = (context) => {
10 | if (!context) {
11 | return 1;
12 | }
13 | const backingStore = context.backingStorePixelRatio ||
14 | context.webkitBackingStorePixelRatio ||
15 | context.mozBackingStorePixelRatio ||
16 | context.msBackingStorePixelRatio ||
17 | context.oBackingStorePixelRatio ||
18 | context.backingStorePixelRatio ||
19 | 1;
20 | return (window.devicePixelRatio || 1) / backingStore;
21 | };
22 | class Watermark {
23 | constructor(options) {
24 | this.option = {
25 | gapX: 212,
26 | gapY: 222,
27 | width: 120,
28 | height: 64,
29 | rotate: -22,
30 | fontStyle: 'normal',
31 | fontWeight: 'normal',
32 | fontColor: 'rgba(0,0,0,.15)',
33 | fontSize: 16,
34 | fontFamily: 'sans-serif',
35 | };
36 | this.option = Object.assign(Object.assign({}, this.option), options);
37 | }
38 | async create() {
39 | const { image = '', content = '', gapX = 212, gapY = 222, width = 120, height = 64, rotate = -22, fontStyle = 'normal', fontWeight = 'normal', fontColor = 'rgba(0,0,0,.15)', fontSize = 16, fontFamily = 'sans-serif', offsetLeft, offsetTop, } = this.option;
40 | const canvas = document.createElement('canvas');
41 | const ctx = canvas.getContext('2d');
42 | const ratio = getPixelRatio(ctx);
43 | const canvasWidth = `${(gapX + width) * ratio}px`;
44 | const canvasHeight = `${(gapY + height) * ratio}px`;
45 | const canvasOffsetLeft = offsetLeft || gapX / 2;
46 | const canvasOffsetTop = offsetTop || gapY / 2;
47 | canvas.setAttribute('width', canvasWidth);
48 | canvas.setAttribute('height', canvasHeight);
49 | return new Promise(async (resolve, reject) => {
50 | if (ctx) {
51 | ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);
52 | ctx.rotate((Math.PI / 180) * Number(rotate));
53 | const markWidth = width * ratio;
54 | const markHeight = height * ratio;
55 | if (image) {
56 | const img = new Image();
57 | img.crossOrigin = 'anonymous';
58 | img.referrerPolicy = 'no-referrer';
59 | img.src = image;
60 | img.onload = async () => {
61 | ctx.drawImage(img, 0, 0, markWidth, markHeight);
62 | return resolve(canvas.toDataURL());
63 | };
64 | img.onerror = (error) => {
65 | return reject(error);
66 | };
67 | }
68 | else if (content) {
69 | const markSize = Number(fontSize) * ratio;
70 | ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
71 | ctx.fillStyle = fontColor;
72 | if (Array.isArray(content)) {
73 | content === null || content === void 0 ? void 0 : content.forEach((item, index) => ctx.fillText(item, 0, index * 50));
74 | }
75 | else {
76 | ctx.fillText(content, 0, 0);
77 | }
78 | return resolve(canvas.toDataURL());
79 | }
80 | }
81 | else {
82 | return reject('Error: Canvas is not supported in the current environment');
83 | }
84 | });
85 | }
86 | }
87 |
88 | module.exports = Watermark;
89 | //# sourceMappingURL=index.cjs.js.map
90 |
--------------------------------------------------------------------------------
/core/dist/index.cjs.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.cjs.js","sources":["../src/index.ts"],"sourcesContent":["export interface WatermarkOptions {\n /** watermark text content */\n content?: string | string[];\n /**\n * When the watermark is drawn, the rotation angle, in `°`. @default `-22`\n */\n rotate?: number;\n /**\n * High-definition print image source, for high-definition screen display,\n * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.\n */\n image?: string;\n /** Horizontal spacing between watermarks. @default `212` */\n gapX?: number;\n /** vertical spacing between watermarks. @default `222` */\n gapY?: number;\n /** width of watermark. @default `120` */\n width?: number;\n /** height of watermark @default `64` */\n height?: number;\n /**\n * The vertical offset of the watermark drawn on the canvas.\n * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`\n */\n offsetLeft?: number;\n /**\n * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,\n * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`\n */\n offsetTop?: number;\n /** text size @default `16` */\n fontSize?: number;\n /** text family @default `sans-serif` */\n fontFamily?: string;\n /** text weight @default `normal` */\n fontWeight?: 'normal' | 'light' | 'weight' | number;\n /** text color @default `rgba(0,0,0,.15)` */\n fontColor?: string;\n /** text style */\n fontStyle?: CanvasFillStrokeStyles['fillStyle'];\n}\n\n/**\n * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution\n * @param context\n * @returns\n */\nconst getPixelRatio = (context: any) => {\n if (!context) {\n return 1;\n }\n const backingStore =\n context.backingStorePixelRatio ||\n context.webkitBackingStorePixelRatio ||\n context.mozBackingStorePixelRatio ||\n context.msBackingStorePixelRatio ||\n context.oBackingStorePixelRatio ||\n context.backingStorePixelRatio ||\n 1;\n return (window.devicePixelRatio || 1) / backingStore;\n};\n\nexport default class Watermark {\n option: WatermarkOptions = {\n gapX: 212,\n gapY: 222,\n width: 120,\n height: 64,\n rotate: -22,\n fontStyle: 'normal',\n fontWeight: 'normal',\n fontColor: 'rgba(0,0,0,.15)',\n fontSize: 16,\n fontFamily: 'sans-serif',\n };\n constructor(options: WatermarkOptions) {\n this.option = { ...this.option, ...options };\n }\n async create(): Promise {\n const {\n image = '',\n content = '',\n gapX = 212,\n gapY = 222,\n width = 120,\n height = 64,\n rotate = -22,\n fontStyle = 'normal',\n fontWeight = 'normal',\n fontColor = 'rgba(0,0,0,.15)',\n fontSize = 16,\n fontFamily = 'sans-serif',\n offsetLeft,\n offsetTop,\n } = this.option;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const ratio = getPixelRatio(ctx);\n const canvasWidth = `${(gapX + width) * ratio}px`;\n const canvasHeight = `${(gapY + height) * ratio}px`;\n const canvasOffsetLeft = offsetLeft || gapX / 2;\n const canvasOffsetTop = offsetTop || gapY / 2;\n canvas.setAttribute('width', canvasWidth);\n canvas.setAttribute('height', canvasHeight);\n return new Promise(async (resolve, reject) => {\n if (ctx) {\n ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);\n ctx.rotate((Math.PI / 180) * Number(rotate));\n const markWidth = width * ratio;\n const markHeight = height * ratio;\n if (image) {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.referrerPolicy = 'no-referrer';\n img.src = image;\n img.onload = async () => {\n ctx.drawImage(img, 0, 0, markWidth, markHeight);\n return resolve(canvas.toDataURL());\n };\n img.onerror = (error) => {\n return reject(error);\n };\n } else if (content) {\n const markSize = Number(fontSize) * ratio;\n ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;\n ctx.fillStyle = fontColor;\n if (Array.isArray(content)) {\n content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50));\n } else {\n ctx.fillText(content, 0, 0);\n }\n return resolve(canvas.toDataURL());\n }\n } else {\n return reject('Error: Canvas is not supported in the current environment');\n }\n });\n }\n}\n"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/core/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export interface WatermarkOptions {
2 | /** watermark text content */
3 | content?: string | string[];
4 | /**
5 | * When the watermark is drawn, the rotation angle, in `°`. @default `-22`
6 | */
7 | rotate?: number;
8 | /**
9 | * High-definition print image source, for high-definition screen display,
10 | * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.
11 | */
12 | image?: string;
13 | /** Horizontal spacing between watermarks. @default `212` */
14 | gapX?: number;
15 | /** vertical spacing between watermarks. @default `222` */
16 | gapY?: number;
17 | /** width of watermark. @default `120` */
18 | width?: number;
19 | /** height of watermark @default `64` */
20 | height?: number;
21 | /**
22 | * The vertical offset of the watermark drawn on the canvas.
23 | * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`
24 | */
25 | offsetLeft?: number;
26 | /**
27 | * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,
28 | * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`
29 | */
30 | offsetTop?: number;
31 | /** text size @default `16` */
32 | fontSize?: number;
33 | /** text family @default `sans-serif` */
34 | fontFamily?: string;
35 | /** text weight @default `normal` */
36 | fontWeight?: 'normal' | 'light' | 'weight' | number;
37 | /** text color @default `rgba(0,0,0,.15)` */
38 | fontColor?: string;
39 | /** text style */
40 | fontStyle?: CanvasFillStrokeStyles['fillStyle'];
41 | }
42 | export default class Watermark {
43 | option: WatermarkOptions;
44 | constructor(options: WatermarkOptions);
45 | create(): Promise;
46 | }
47 |
--------------------------------------------------------------------------------
/core/dist/index.esm.js:
--------------------------------------------------------------------------------
1 | /*! @uiw/watermark.js v1.0.1 | MIT © 2024 kenny wang https://uiwjs.github.io/react-watermark */
2 | /**
3 | * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution
4 | * @param context
5 | * @returns
6 | */
7 | const getPixelRatio = (context) => {
8 | if (!context) {
9 | return 1;
10 | }
11 | const backingStore = context.backingStorePixelRatio ||
12 | context.webkitBackingStorePixelRatio ||
13 | context.mozBackingStorePixelRatio ||
14 | context.msBackingStorePixelRatio ||
15 | context.oBackingStorePixelRatio ||
16 | context.backingStorePixelRatio ||
17 | 1;
18 | return (window.devicePixelRatio || 1) / backingStore;
19 | };
20 | class Watermark {
21 | constructor(options) {
22 | this.option = {
23 | gapX: 212,
24 | gapY: 222,
25 | width: 120,
26 | height: 64,
27 | rotate: -22,
28 | fontStyle: 'normal',
29 | fontWeight: 'normal',
30 | fontColor: 'rgba(0,0,0,.15)',
31 | fontSize: 16,
32 | fontFamily: 'sans-serif',
33 | };
34 | this.option = Object.assign(Object.assign({}, this.option), options);
35 | }
36 | async create() {
37 | const { image = '', content = '', gapX = 212, gapY = 222, width = 120, height = 64, rotate = -22, fontStyle = 'normal', fontWeight = 'normal', fontColor = 'rgba(0,0,0,.15)', fontSize = 16, fontFamily = 'sans-serif', offsetLeft, offsetTop, } = this.option;
38 | const canvas = document.createElement('canvas');
39 | const ctx = canvas.getContext('2d');
40 | const ratio = getPixelRatio(ctx);
41 | const canvasWidth = `${(gapX + width) * ratio}px`;
42 | const canvasHeight = `${(gapY + height) * ratio}px`;
43 | const canvasOffsetLeft = offsetLeft || gapX / 2;
44 | const canvasOffsetTop = offsetTop || gapY / 2;
45 | canvas.setAttribute('width', canvasWidth);
46 | canvas.setAttribute('height', canvasHeight);
47 | return new Promise(async (resolve, reject) => {
48 | if (ctx) {
49 | ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);
50 | ctx.rotate((Math.PI / 180) * Number(rotate));
51 | const markWidth = width * ratio;
52 | const markHeight = height * ratio;
53 | if (image) {
54 | const img = new Image();
55 | img.crossOrigin = 'anonymous';
56 | img.referrerPolicy = 'no-referrer';
57 | img.src = image;
58 | img.onload = async () => {
59 | ctx.drawImage(img, 0, 0, markWidth, markHeight);
60 | return resolve(canvas.toDataURL());
61 | };
62 | img.onerror = (error) => {
63 | return reject(error);
64 | };
65 | }
66 | else if (content) {
67 | const markSize = Number(fontSize) * ratio;
68 | ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
69 | ctx.fillStyle = fontColor;
70 | if (Array.isArray(content)) {
71 | content === null || content === void 0 ? void 0 : content.forEach((item, index) => ctx.fillText(item, 0, index * 50));
72 | }
73 | else {
74 | ctx.fillText(content, 0, 0);
75 | }
76 | return resolve(canvas.toDataURL());
77 | }
78 | }
79 | else {
80 | return reject('Error: Canvas is not supported in the current environment');
81 | }
82 | });
83 | }
84 | }
85 |
86 | export { Watermark as default };
87 | //# sourceMappingURL=index.esm.js.map
88 |
--------------------------------------------------------------------------------
/core/dist/index.esm.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.esm.js","sources":["../src/index.ts"],"sourcesContent":["export interface WatermarkOptions {\n /** watermark text content */\n content?: string | string[];\n /**\n * When the watermark is drawn, the rotation angle, in `°`. @default `-22`\n */\n rotate?: number;\n /**\n * High-definition print image source, for high-definition screen display,\n * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.\n */\n image?: string;\n /** Horizontal spacing between watermarks. @default `212` */\n gapX?: number;\n /** vertical spacing between watermarks. @default `222` */\n gapY?: number;\n /** width of watermark. @default `120` */\n width?: number;\n /** height of watermark @default `64` */\n height?: number;\n /**\n * The vertical offset of the watermark drawn on the canvas.\n * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`\n */\n offsetLeft?: number;\n /**\n * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,\n * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`\n */\n offsetTop?: number;\n /** text size @default `16` */\n fontSize?: number;\n /** text family @default `sans-serif` */\n fontFamily?: string;\n /** text weight @default `normal` */\n fontWeight?: 'normal' | 'light' | 'weight' | number;\n /** text color @default `rgba(0,0,0,.15)` */\n fontColor?: string;\n /** text style */\n fontStyle?: CanvasFillStrokeStyles['fillStyle'];\n}\n\n/**\n * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution\n * @param context\n * @returns\n */\nconst getPixelRatio = (context: any) => {\n if (!context) {\n return 1;\n }\n const backingStore =\n context.backingStorePixelRatio ||\n context.webkitBackingStorePixelRatio ||\n context.mozBackingStorePixelRatio ||\n context.msBackingStorePixelRatio ||\n context.oBackingStorePixelRatio ||\n context.backingStorePixelRatio ||\n 1;\n return (window.devicePixelRatio || 1) / backingStore;\n};\n\nexport default class Watermark {\n option: WatermarkOptions = {\n gapX: 212,\n gapY: 222,\n width: 120,\n height: 64,\n rotate: -22,\n fontStyle: 'normal',\n fontWeight: 'normal',\n fontColor: 'rgba(0,0,0,.15)',\n fontSize: 16,\n fontFamily: 'sans-serif',\n };\n constructor(options: WatermarkOptions) {\n this.option = { ...this.option, ...options };\n }\n async create(): Promise {\n const {\n image = '',\n content = '',\n gapX = 212,\n gapY = 222,\n width = 120,\n height = 64,\n rotate = -22,\n fontStyle = 'normal',\n fontWeight = 'normal',\n fontColor = 'rgba(0,0,0,.15)',\n fontSize = 16,\n fontFamily = 'sans-serif',\n offsetLeft,\n offsetTop,\n } = this.option;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const ratio = getPixelRatio(ctx);\n const canvasWidth = `${(gapX + width) * ratio}px`;\n const canvasHeight = `${(gapY + height) * ratio}px`;\n const canvasOffsetLeft = offsetLeft || gapX / 2;\n const canvasOffsetTop = offsetTop || gapY / 2;\n canvas.setAttribute('width', canvasWidth);\n canvas.setAttribute('height', canvasHeight);\n return new Promise(async (resolve, reject) => {\n if (ctx) {\n ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);\n ctx.rotate((Math.PI / 180) * Number(rotate));\n const markWidth = width * ratio;\n const markHeight = height * ratio;\n if (image) {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.referrerPolicy = 'no-referrer';\n img.src = image;\n img.onload = async () => {\n ctx.drawImage(img, 0, 0, markWidth, markHeight);\n return resolve(canvas.toDataURL());\n };\n img.onerror = (error) => {\n return reject(error);\n };\n } else if (content) {\n const markSize = Number(fontSize) * ratio;\n ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;\n ctx.fillStyle = fontColor;\n if (Array.isArray(content)) {\n content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50));\n } else {\n ctx.fillText(content, 0, 0);\n }\n return resolve(canvas.toDataURL());\n }\n } else {\n return reject('Error: Canvas is not supported in the current environment');\n }\n });\n }\n}\n"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/core/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Watermark
8 |
9 |
10 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
47 |
48 |
--------------------------------------------------------------------------------
/core/dist/watermark.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * @uiw/watermark.js v1.0.1
3 | * JavaScript library for generating image watermarks using canvas.
4 | *
5 | * Copyright (c) 2024 kenny wang
6 | * https://github.com/uiwjs/react-watermark.git
7 | *
8 | * @website: https://uiwjs.github.io/react-watermark
9 |
10 | * Licensed under the MIT license
11 | */
12 |
13 | (function (global, factory) {
14 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
15 | typeof define === 'function' && define.amd ? define(factory) :
16 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Watermark = factory());
17 | })(this, (function () { 'use strict';
18 |
19 | /**
20 | * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution
21 | * @param context
22 | * @returns
23 | */
24 | const getPixelRatio = (context) => {
25 | if (!context) {
26 | return 1;
27 | }
28 | const backingStore = context.backingStorePixelRatio ||
29 | context.webkitBackingStorePixelRatio ||
30 | context.mozBackingStorePixelRatio ||
31 | context.msBackingStorePixelRatio ||
32 | context.oBackingStorePixelRatio ||
33 | context.backingStorePixelRatio ||
34 | 1;
35 | return (window.devicePixelRatio || 1) / backingStore;
36 | };
37 | class Watermark {
38 | constructor(options) {
39 | this.option = {
40 | gapX: 212,
41 | gapY: 222,
42 | width: 120,
43 | height: 64,
44 | rotate: -22,
45 | fontStyle: 'normal',
46 | fontWeight: 'normal',
47 | fontColor: 'rgba(0,0,0,.15)',
48 | fontSize: 16,
49 | fontFamily: 'sans-serif',
50 | };
51 | this.option = Object.assign(Object.assign({}, this.option), options);
52 | }
53 | async create() {
54 | const { image = '', content = '', gapX = 212, gapY = 222, width = 120, height = 64, rotate = -22, fontStyle = 'normal', fontWeight = 'normal', fontColor = 'rgba(0,0,0,.15)', fontSize = 16, fontFamily = 'sans-serif', offsetLeft, offsetTop, } = this.option;
55 | const canvas = document.createElement('canvas');
56 | const ctx = canvas.getContext('2d');
57 | const ratio = getPixelRatio(ctx);
58 | const canvasWidth = `${(gapX + width) * ratio}px`;
59 | const canvasHeight = `${(gapY + height) * ratio}px`;
60 | const canvasOffsetLeft = offsetLeft || gapX / 2;
61 | const canvasOffsetTop = offsetTop || gapY / 2;
62 | canvas.setAttribute('width', canvasWidth);
63 | canvas.setAttribute('height', canvasHeight);
64 | return new Promise(async (resolve, reject) => {
65 | if (ctx) {
66 | ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);
67 | ctx.rotate((Math.PI / 180) * Number(rotate));
68 | const markWidth = width * ratio;
69 | const markHeight = height * ratio;
70 | if (image) {
71 | const img = new Image();
72 | img.crossOrigin = 'anonymous';
73 | img.referrerPolicy = 'no-referrer';
74 | img.src = image;
75 | img.onload = async () => {
76 | ctx.drawImage(img, 0, 0, markWidth, markHeight);
77 | return resolve(canvas.toDataURL());
78 | };
79 | img.onerror = (error) => {
80 | return reject(error);
81 | };
82 | }
83 | else if (content) {
84 | const markSize = Number(fontSize) * ratio;
85 | ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
86 | ctx.fillStyle = fontColor;
87 | if (Array.isArray(content)) {
88 | content === null || content === void 0 ? void 0 : content.forEach((item, index) => ctx.fillText(item, 0, index * 50));
89 | }
90 | else {
91 | ctx.fillText(content, 0, 0);
92 | }
93 | return resolve(canvas.toDataURL());
94 | }
95 | }
96 | else {
97 | return reject('Error: Canvas is not supported in the current environment');
98 | }
99 | });
100 | }
101 | }
102 |
103 | return Watermark;
104 |
105 | }));
106 | //# sourceMappingURL=watermark.js.map
107 |
--------------------------------------------------------------------------------
/core/dist/watermark.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"watermark.js","sources":["../src/index.ts"],"sourcesContent":["export interface WatermarkOptions {\n /** watermark text content */\n content?: string | string[];\n /**\n * When the watermark is drawn, the rotation angle, in `°`. @default `-22`\n */\n rotate?: number;\n /**\n * High-definition print image source, for high-definition screen display,\n * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.\n */\n image?: string;\n /** Horizontal spacing between watermarks. @default `212` */\n gapX?: number;\n /** vertical spacing between watermarks. @default `222` */\n gapY?: number;\n /** width of watermark. @default `120` */\n width?: number;\n /** height of watermark @default `64` */\n height?: number;\n /**\n * The vertical offset of the watermark drawn on the canvas.\n * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`\n */\n offsetLeft?: number;\n /**\n * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,\n * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`\n */\n offsetTop?: number;\n /** text size @default `16` */\n fontSize?: number;\n /** text family @default `sans-serif` */\n fontFamily?: string;\n /** text weight @default `normal` */\n fontWeight?: 'normal' | 'light' | 'weight' | number;\n /** text color @default `rgba(0,0,0,.15)` */\n fontColor?: string;\n /** text style */\n fontStyle?: CanvasFillStrokeStyles['fillStyle'];\n}\n\n/**\n * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution\n * @param context\n * @returns\n */\nconst getPixelRatio = (context: any) => {\n if (!context) {\n return 1;\n }\n const backingStore =\n context.backingStorePixelRatio ||\n context.webkitBackingStorePixelRatio ||\n context.mozBackingStorePixelRatio ||\n context.msBackingStorePixelRatio ||\n context.oBackingStorePixelRatio ||\n context.backingStorePixelRatio ||\n 1;\n return (window.devicePixelRatio || 1) / backingStore;\n};\n\nexport default class Watermark {\n option: WatermarkOptions = {\n gapX: 212,\n gapY: 222,\n width: 120,\n height: 64,\n rotate: -22,\n fontStyle: 'normal',\n fontWeight: 'normal',\n fontColor: 'rgba(0,0,0,.15)',\n fontSize: 16,\n fontFamily: 'sans-serif',\n };\n constructor(options: WatermarkOptions) {\n this.option = { ...this.option, ...options };\n }\n async create(): Promise {\n const {\n image = '',\n content = '',\n gapX = 212,\n gapY = 222,\n width = 120,\n height = 64,\n rotate = -22,\n fontStyle = 'normal',\n fontWeight = 'normal',\n fontColor = 'rgba(0,0,0,.15)',\n fontSize = 16,\n fontFamily = 'sans-serif',\n offsetLeft,\n offsetTop,\n } = this.option;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const ratio = getPixelRatio(ctx);\n const canvasWidth = `${(gapX + width) * ratio}px`;\n const canvasHeight = `${(gapY + height) * ratio}px`;\n const canvasOffsetLeft = offsetLeft || gapX / 2;\n const canvasOffsetTop = offsetTop || gapY / 2;\n canvas.setAttribute('width', canvasWidth);\n canvas.setAttribute('height', canvasHeight);\n return new Promise(async (resolve, reject) => {\n if (ctx) {\n ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);\n ctx.rotate((Math.PI / 180) * Number(rotate));\n const markWidth = width * ratio;\n const markHeight = height * ratio;\n if (image) {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.referrerPolicy = 'no-referrer';\n img.src = image;\n img.onload = async () => {\n ctx.drawImage(img, 0, 0, markWidth, markHeight);\n return resolve(canvas.toDataURL());\n };\n img.onerror = (error) => {\n return reject(error);\n };\n } else if (content) {\n const markSize = Number(fontSize) * ratio;\n ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;\n ctx.fillStyle = fontColor;\n if (Array.isArray(content)) {\n content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50));\n } else {\n ctx.fillText(content, 0, 0);\n }\n return resolve(canvas.toDataURL());\n }\n } else {\n return reject('Error: Canvas is not supported in the current environment');\n }\n });\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;IA0CA;;;;IAIG;IACH,MAAM,aAAa,GAAG,CAAC,OAAY,KAAI;QACrC,IAAI,CAAC,OAAO,EAAE;IACZ,QAAA,OAAO,CAAC,CAAC;IACV,KAAA;IACD,IAAA,MAAM,YAAY,GAChB,OAAO,CAAC,sBAAsB;IAC9B,QAAA,OAAO,CAAC,4BAA4B;IACpC,QAAA,OAAO,CAAC,yBAAyB;IACjC,QAAA,OAAO,CAAC,wBAAwB;IAChC,QAAA,OAAO,CAAC,uBAAuB;IAC/B,QAAA,OAAO,CAAC,sBAAsB;IAC9B,QAAA,CAAC,CAAC;QACJ,OAAO,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,IAAI,YAAY,CAAC;IACvD,CAAC,CAAC;IAEY,MAAO,SAAS,CAAA;IAa5B,IAAA,WAAA,CAAY,OAAyB,EAAA;IAZrC,QAAA,IAAA,CAAA,MAAM,GAAqB;IACzB,YAAA,IAAI,EAAE,GAAG;IACT,YAAA,IAAI,EAAE,GAAG;IACT,YAAA,KAAK,EAAE,GAAG;IACV,YAAA,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,CAAC,EAAE;IACX,YAAA,SAAS,EAAE,QAAQ;IACnB,YAAA,UAAU,EAAE,QAAQ;IACpB,YAAA,SAAS,EAAE,iBAAiB;IAC5B,YAAA,QAAQ,EAAE,EAAE;IACZ,YAAA,UAAU,EAAE,YAAY;aACzB,CAAC;YAEA,IAAI,CAAC,MAAM,GAAQ,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,EAAA,EAAA,IAAI,CAAC,MAAM,CAAA,EAAK,OAAO,CAAE,CAAC;SAC9C;IACD,IAAA,MAAM,MAAM,GAAA;YACV,MAAM,EACJ,KAAK,GAAG,EAAE,EACV,OAAO,GAAG,EAAE,EACZ,IAAI,GAAG,GAAG,EACV,IAAI,GAAG,GAAG,EACV,KAAK,GAAG,GAAG,EACX,MAAM,GAAG,EAAE,EACX,MAAM,GAAG,CAAC,EAAE,EACZ,SAAS,GAAG,QAAQ,EACpB,UAAU,GAAG,QAAQ,EACrB,SAAS,GAAG,iBAAiB,EAC7B,QAAQ,GAAG,EAAE,EACb,UAAU,GAAG,YAAY,EACzB,UAAU,EACV,SAAS,GACV,GAAG,IAAI,CAAC,MAAM,CAAC;YAChB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAChD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,QAAA,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,WAAW,GAAG,CAAA,EAAG,CAAC,IAAI,GAAG,KAAK,IAAI,KAAK,CAAA,EAAA,CAAI,CAAC;YAClD,MAAM,YAAY,GAAG,CAAA,EAAG,CAAC,IAAI,GAAG,MAAM,IAAI,KAAK,CAAA,EAAA,CAAI,CAAC;IACpD,QAAA,MAAM,gBAAgB,GAAG,UAAU,IAAI,IAAI,GAAG,CAAC,CAAC;IAChD,QAAA,MAAM,eAAe,GAAG,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC;IAC9C,QAAA,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC1C,QAAA,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YAC5C,OAAO,IAAI,OAAO,CAAC,OAAO,OAAO,EAAE,MAAM,KAAI;IAC3C,YAAA,IAAI,GAAG,EAAE;oBACP,GAAG,CAAC,SAAS,CAAC,gBAAgB,GAAG,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,CAAC;IACjE,gBAAA,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,gBAAA,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;IAChC,gBAAA,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;IAClC,gBAAA,IAAI,KAAK,EAAE;IACT,oBAAA,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;IACxB,oBAAA,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;IAC9B,oBAAA,GAAG,CAAC,cAAc,GAAG,aAAa,CAAC;IACnC,oBAAA,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC;IAChB,oBAAA,GAAG,CAAC,MAAM,GAAG,YAAW;IACtB,wBAAA,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,wBAAA,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACrC,qBAAC,CAAC;IACF,oBAAA,GAAG,CAAC,OAAO,GAAG,CAAC,KAAK,KAAI;IACtB,wBAAA,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,qBAAC,CAAC;IACH,iBAAA;IAAM,qBAAA,IAAI,OAAO,EAAE;wBAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAC1C,oBAAA,GAAG,CAAC,IAAI,GAAG,CAAA,EAAG,SAAS,CAAW,QAAA,EAAA,UAAU,CAAI,CAAA,EAAA,QAAQ,CAAM,GAAA,EAAA,UAAU,CAAM,GAAA,EAAA,UAAU,EAAE,CAAC;IAC3F,oBAAA,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;IAC1B,oBAAA,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;4BAC1B,OAAO,KAAA,IAAA,IAAP,OAAO,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAP,OAAO,CAAE,OAAO,CAAC,CAAC,IAAY,EAAE,KAAa,KAAK,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;IACtF,qBAAA;IAAM,yBAAA;4BACL,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,qBAAA;IACD,oBAAA,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACpC,iBAAA;IACF,aAAA;IAAM,iBAAA;IACL,gBAAA,OAAO,MAAM,CAAC,2DAA2D,CAAC,CAAC;IAC5E,aAAA;IACH,SAAC,CAAC,CAAC;SACJ;IACF;;;;;;;;"}
--------------------------------------------------------------------------------
/core/dist/watermark.min.js:
--------------------------------------------------------------------------------
1 | /*! @uiw/watermark.js v1.0.1 | MIT © 2024 kenny wang https://uiwjs.github.io/react-watermark */
2 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Watermark=e()}(this,(function(){"use strict";return class{constructor(t){this.option={gapX:212,gapY:222,width:120,height:64,rotate:-22,fontStyle:"normal",fontWeight:"normal",fontColor:"rgba(0,0,0,.15)",fontSize:16,fontFamily:"sans-serif"},this.option=Object.assign(Object.assign({},this.option),t)}async create(){const{image:t="",content:e="",gapX:o=212,gapY:n=222,width:i=120,height:r=64,rotate:a=-22,fontStyle:s="normal",fontWeight:f="normal",fontColor:l="rgba(0,0,0,.15)",fontSize:c=16,fontFamily:g="sans-serif",offsetLeft:u,offsetTop:m}=this.option,h=document.createElement("canvas"),p=h.getContext("2d"),d=(t=>{if(!t)return 1;const e=t.backingStorePixelRatio||t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return(window.devicePixelRatio||1)/e})(p),x=(o+i)*d+"px",y=(n+r)*d+"px",b=u||o/2,S=m||n/2;return h.setAttribute("width",x),h.setAttribute("height",y),new Promise((async(o,n)=>{if(!p)return n("Error: Canvas is not supported in the current environment");{p.translate(b*d,S*d),p.rotate(Math.PI/180*Number(a));const u=i*d,m=r*d;if(t){const e=new Image;e.crossOrigin="anonymous",e.referrerPolicy="no-referrer",e.src=t,e.onload=async()=>(p.drawImage(e,0,0,u,m),o(h.toDataURL())),e.onerror=t=>n(t)}else if(e){const t=Number(c)*d;return p.font=`${s} normal ${f} ${t}px/${m}px ${g}`,p.fillStyle=l,Array.isArray(e)?null==e||e.forEach(((t,e)=>p.fillText(t,0,50*e))):p.fillText(e,0,0),o(h.toDataURL())}}}))}}}));
3 | //# sourceMappingURL=watermark.min.js.map
4 |
--------------------------------------------------------------------------------
/core/dist/watermark.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"watermark.min.js","sources":["../src/index.ts"],"sourcesContent":["export interface WatermarkOptions {\n /** watermark text content */\n content?: string | string[];\n /**\n * When the watermark is drawn, the rotation angle, in `°`. @default `-22`\n */\n rotate?: number;\n /**\n * High-definition print image source, for high-definition screen display,\n * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.\n */\n image?: string;\n /** Horizontal spacing between watermarks. @default `212` */\n gapX?: number;\n /** vertical spacing between watermarks. @default `222` */\n gapY?: number;\n /** width of watermark. @default `120` */\n width?: number;\n /** height of watermark @default `64` */\n height?: number;\n /**\n * The vertical offset of the watermark drawn on the canvas.\n * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`\n */\n offsetLeft?: number;\n /**\n * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,\n * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`\n */\n offsetTop?: number;\n /** text size @default `16` */\n fontSize?: number;\n /** text family @default `sans-serif` */\n fontFamily?: string;\n /** text weight @default `normal` */\n fontWeight?: 'normal' | 'light' | 'weight' | number;\n /** text color @default `rgba(0,0,0,.15)` */\n fontColor?: string;\n /** text style */\n fontStyle?: CanvasFillStrokeStyles['fillStyle'];\n}\n\n/**\n * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution\n * @param context\n * @returns\n */\nconst getPixelRatio = (context: any) => {\n if (!context) {\n return 1;\n }\n const backingStore =\n context.backingStorePixelRatio ||\n context.webkitBackingStorePixelRatio ||\n context.mozBackingStorePixelRatio ||\n context.msBackingStorePixelRatio ||\n context.oBackingStorePixelRatio ||\n context.backingStorePixelRatio ||\n 1;\n return (window.devicePixelRatio || 1) / backingStore;\n};\n\nexport default class Watermark {\n option: WatermarkOptions = {\n gapX: 212,\n gapY: 222,\n width: 120,\n height: 64,\n rotate: -22,\n fontStyle: 'normal',\n fontWeight: 'normal',\n fontColor: 'rgba(0,0,0,.15)',\n fontSize: 16,\n fontFamily: 'sans-serif',\n };\n constructor(options: WatermarkOptions) {\n this.option = { ...this.option, ...options };\n }\n async create(): Promise {\n const {\n image = '',\n content = '',\n gapX = 212,\n gapY = 222,\n width = 120,\n height = 64,\n rotate = -22,\n fontStyle = 'normal',\n fontWeight = 'normal',\n fontColor = 'rgba(0,0,0,.15)',\n fontSize = 16,\n fontFamily = 'sans-serif',\n offsetLeft,\n offsetTop,\n } = this.option;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const ratio = getPixelRatio(ctx);\n const canvasWidth = `${(gapX + width) * ratio}px`;\n const canvasHeight = `${(gapY + height) * ratio}px`;\n const canvasOffsetLeft = offsetLeft || gapX / 2;\n const canvasOffsetTop = offsetTop || gapY / 2;\n canvas.setAttribute('width', canvasWidth);\n canvas.setAttribute('height', canvasHeight);\n return new Promise(async (resolve, reject) => {\n if (ctx) {\n ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);\n ctx.rotate((Math.PI / 180) * Number(rotate));\n const markWidth = width * ratio;\n const markHeight = height * ratio;\n if (image) {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.referrerPolicy = 'no-referrer';\n img.src = image;\n img.onload = async () => {\n ctx.drawImage(img, 0, 0, markWidth, markHeight);\n return resolve(canvas.toDataURL());\n };\n img.onerror = (error) => {\n return reject(error);\n };\n } else if (content) {\n const markSize = Number(fontSize) * ratio;\n ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;\n ctx.fillStyle = fontColor;\n if (Array.isArray(content)) {\n content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50));\n } else {\n ctx.fillText(content, 0, 0);\n }\n return resolve(canvas.toDataURL());\n }\n } else {\n return reject('Error: Canvas is not supported in the current environment');\n }\n });\n }\n}\n"],"names":["constructor","options","this","option","gapX","gapY","width","height","rotate","fontStyle","fontWeight","fontColor","fontSize","fontFamily","Object","assign","create","image","content","offsetLeft","offsetTop","canvas","document","createElement","ctx","getContext","ratio","context","backingStore","backingStorePixelRatio","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","window","devicePixelRatio","getPixelRatio","canvasWidth","canvasHeight","canvasOffsetLeft","canvasOffsetTop","setAttribute","Promise","async","resolve","reject","translate","Math","PI","Number","markWidth","markHeight","img","Image","crossOrigin","referrerPolicy","src","onload","drawImage","toDataURL","onerror","error","markSize","font","fillStyle","Array","isArray","forEach","item","index","fillText"],"mappings":";iPA8Dc,MAaZ,WAAAA,CAAYC,GAZZC,KAAAC,OAA2B,CACzBC,KAAM,IACNC,KAAM,IACNC,MAAO,IACPC,OAAQ,GACRC,QAAS,GACTC,UAAW,SACXC,WAAY,SACZC,UAAW,kBACXC,SAAU,GACVC,WAAY,cAGZX,KAAKC,OAAcW,OAAAC,OAAAD,OAAAC,OAAA,CAAA,EAAAb,KAAKC,QAAWF,EACpC,CACD,YAAMe,GACJ,MAAMC,MACJA,EAAQ,GAAEC,QACVA,EAAU,GAAEd,KACZA,EAAO,IAAGC,KACVA,EAAO,IAAGC,MACVA,EAAQ,IAAGC,OACXA,EAAS,GAAEC,OACXA,GAAS,GAAGC,UACZA,EAAY,SAAQC,WACpBA,EAAa,SAAQC,UACrBA,EAAY,kBAAiBC,SAC7BA,EAAW,GAAEC,WACbA,EAAa,aAAYM,WACzBA,EAAUC,UACVA,GACElB,KAAKC,OACHkB,EAASC,SAASC,cAAc,UAChCC,EAAMH,EAAOI,WAAW,MACxBC,EAlDY,CAACC,IACrB,IAAKA,EACH,OAAO,EAET,MAAMC,EACJD,EAAQE,wBACRF,EAAQG,8BACRH,EAAQI,2BACRJ,EAAQK,0BACRL,EAAQM,yBACRN,EAAQE,wBACR,EACF,OAAQK,OAAOC,kBAAoB,GAAKP,CAAY,EAsCpCQ,CAAcZ,GACtBa,GAAkBjC,EAAOE,GAASoB,EAApB,KACdY,GAAmBjC,EAAOE,GAAUmB,EAArB,KACfa,EAAmBpB,GAAcf,EAAO,EACxCoC,EAAkBpB,GAAaf,EAAO,EAG5C,OAFAgB,EAAOoB,aAAa,QAASJ,GAC7BhB,EAAOoB,aAAa,SAAUH,GACvB,IAAII,SAAQC,MAAOC,EAASC,KACjC,IAAIrB,EA6BF,OAAOqB,EAAO,6DA7BP,CACPrB,EAAIsB,UAAUP,EAAmBb,EAAOc,EAAkBd,GAC1DF,EAAIhB,OAAQuC,KAAKC,GAAK,IAAOC,OAAOzC,IACpC,MAAM0C,EAAY5C,EAAQoB,EACpByB,EAAa5C,EAASmB,EAC5B,GAAIT,EAAO,CACT,MAAMmC,EAAM,IAAIC,MAChBD,EAAIE,YAAc,YAClBF,EAAIG,eAAiB,cACrBH,EAAII,IAAMvC,EACVmC,EAAIK,OAASd,UACXnB,EAAIkC,UAAUN,EAAK,EAAG,EAAGF,EAAWC,GAC7BP,EAAQvB,EAAOsC,cAExBP,EAAIQ,QAAWC,GACNhB,EAAOgB,EAEjB,MAAM,GAAI3C,EAAS,CAClB,MAAM4C,EAAWb,OAAOrC,GAAYc,EAQpC,OAPAF,EAAIuC,KAAO,GAAGtD,YAAoBC,KAAcoD,OAAcX,OAAgBtC,IAC9EW,EAAIwC,UAAYrD,EACZsD,MAAMC,QAAQhD,GAChBA,SAAAA,EAASiD,SAAQ,CAACC,EAAcC,IAAkB7C,EAAI8C,SAASF,EAAM,EAAW,GAARC,KAExE7C,EAAI8C,SAASpD,EAAS,EAAG,GAEpB0B,EAAQvB,EAAOsC,YACvB,CACF,CAEA,GAEJ"}
--------------------------------------------------------------------------------
/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uiw/watermark.js",
3 | "version": "1.0.1",
4 | "homepage": "https://uiwjs.github.io/react-watermark",
5 | "funding": "https://jaywcjlove.github.io/#/sponsor",
6 | "author": "kenny wang ",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/uiwjs/react-watermark.git"
10 | },
11 | "license": "MIT",
12 | "description": "JavaScript library for generating image watermarks using canvas.",
13 | "main": "./dist/index.cjs.js",
14 | "module": "./dist/index.esm.js",
15 | "typings": "./dist/index.d.ts",
16 | "unpkg": "./dist/watermark.js",
17 | "jsdelivr": "./dist/watermark.js",
18 | "scripts": {
19 | "build": "rollup -c rollup.config.ts --configPlugin typescript",
20 | "start": "rollup -c rollup.config.ts --configPlugin typescript -w"
21 | },
22 | "keywords": [],
23 | "files": [
24 | "src",
25 | "dist",
26 | "!dist/index.html"
27 | ],
28 | "devDependencies": {
29 | "@rollup/plugin-commonjs": "^25.0.2",
30 | "@rollup/plugin-json": "^6.0.0",
31 | "@rollup/plugin-node-resolve": "^15.1.0",
32 | "@rollup/plugin-terser": "^0.4.3",
33 | "@rollup/plugin-typescript": "^11.1.2",
34 | "bannerjs": "^3.0.1",
35 | "rollup": "^3.26.1",
36 | "tslib": "^2.6.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/rollup.config.ts:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'module';
2 | import typescript from '@rollup/plugin-typescript';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import { nodeResolve } from '@rollup/plugin-node-resolve';
5 | import terser from '@rollup/plugin-terser';
6 | import { multibanner, onebanner } from 'bannerjs';
7 |
8 | const require = createRequire(import.meta.url);
9 | const pkg = require('./package.json');
10 |
11 | export default [
12 | {
13 | input: 'src/index.ts',
14 | output: [
15 | {
16 | file: pkg.unpkg,
17 | format: 'umd',
18 | exports: 'default',
19 | name: 'Watermark',
20 | banner: multibanner(),
21 | sourcemap: true,
22 | },
23 | {
24 | file: pkg.main,
25 | format: 'cjs',
26 | exports: 'default',
27 | name: 'Watermark',
28 | banner: onebanner(),
29 | sourcemap: true,
30 | },
31 | {
32 | file: pkg.module,
33 | format: 'esm',
34 | exports: 'default',
35 | name: 'Watermark',
36 | banner: onebanner(),
37 | sourcemap: true,
38 | },
39 | ],
40 | plugins: [
41 | nodeResolve({
42 | browser: true,
43 | }),
44 | commonjs(),
45 | typescript({
46 | tsconfig: './tsconfig.json',
47 | compilerOptions: {
48 | outDir: 'dist',
49 | declarationDir: '.',
50 | },
51 | }),
52 | ],
53 | },
54 | {
55 | input: 'src/index.ts',
56 | output: [
57 | {
58 | file: pkg.unpkg.replace(/.js$/, '.min.js'),
59 | format: 'umd',
60 | exports: 'default',
61 | name: 'Watermark',
62 | banner: onebanner(),
63 | sourcemap: true,
64 | },
65 | ],
66 | plugins: [
67 | nodeResolve({
68 | browser: true,
69 | }),
70 | commonjs(),
71 | typescript({
72 | tsconfig: './tsconfig.json',
73 | compilerOptions: {
74 | outDir: 'dist',
75 | declarationDir: '.',
76 | },
77 | }),
78 | terser({}),
79 | ],
80 | },
81 | ];
82 |
--------------------------------------------------------------------------------
/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export interface WatermarkOptions {
2 | /** watermark text content */
3 | content?: string | string[];
4 | /**
5 | * When the watermark is drawn, the rotation angle, in `°`. @default `-22`
6 | */
7 | rotate?: number;
8 | /**
9 | * High-definition print image source, for high-definition screen display,
10 | * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.
11 | */
12 | image?: string;
13 | /** Horizontal spacing between watermarks. @default `212` */
14 | gapX?: number;
15 | /** vertical spacing between watermarks. @default `222` */
16 | gapY?: number;
17 | /** width of watermark. @default `120` */
18 | width?: number;
19 | /** height of watermark @default `64` */
20 | height?: number;
21 | /**
22 | * The vertical offset of the watermark drawn on the canvas.
23 | * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`
24 | */
25 | offsetLeft?: number;
26 | /**
27 | * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,
28 | * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`
29 | */
30 | offsetTop?: number;
31 | /** text size @default `16` */
32 | fontSize?: number;
33 | /** text family @default `sans-serif` */
34 | fontFamily?: string;
35 | /** text weight @default `normal` */
36 | fontWeight?: 'normal' | 'light' | 'weight' | number;
37 | /** text color @default `rgba(0,0,0,.15)` */
38 | fontColor?: string;
39 | /** text style */
40 | fontStyle?: CanvasFillStrokeStyles['fillStyle'];
41 | }
42 |
43 | /**
44 | * Returns the ratio of the current display device's physical pixel resolution to CSS pixel resolution
45 | * @param context
46 | * @returns
47 | */
48 | const getPixelRatio = (context: any) => {
49 | if (!context) {
50 | return 1;
51 | }
52 | const backingStore =
53 | context.backingStorePixelRatio ||
54 | context.webkitBackingStorePixelRatio ||
55 | context.mozBackingStorePixelRatio ||
56 | context.msBackingStorePixelRatio ||
57 | context.oBackingStorePixelRatio ||
58 | context.backingStorePixelRatio ||
59 | 1;
60 | return (window.devicePixelRatio || 1) / backingStore;
61 | };
62 |
63 | export default class Watermark {
64 | option: WatermarkOptions = {
65 | gapX: 212,
66 | gapY: 222,
67 | width: 120,
68 | height: 64,
69 | rotate: -22,
70 | fontStyle: 'normal',
71 | fontWeight: 'normal',
72 | fontColor: 'rgba(0,0,0,.15)',
73 | fontSize: 16,
74 | fontFamily: 'sans-serif',
75 | };
76 | constructor(options: WatermarkOptions) {
77 | this.option = { ...this.option, ...options };
78 | }
79 | async create(): Promise {
80 | const {
81 | image = '',
82 | content = '',
83 | gapX = 212,
84 | gapY = 222,
85 | width = 120,
86 | height = 64,
87 | rotate = -22,
88 | fontStyle = 'normal',
89 | fontWeight = 'normal',
90 | fontColor = 'rgba(0,0,0,.15)',
91 | fontSize = 16,
92 | fontFamily = 'sans-serif',
93 | offsetLeft,
94 | offsetTop,
95 | } = this.option;
96 | const canvas = document.createElement('canvas');
97 | const ctx = canvas.getContext('2d');
98 | const ratio = getPixelRatio(ctx);
99 | const canvasWidth = `${(gapX + width) * ratio}px`;
100 | const canvasHeight = `${(gapY + height) * ratio}px`;
101 | const canvasOffsetLeft = offsetLeft || gapX / 2;
102 | const canvasOffsetTop = offsetTop || gapY / 2;
103 | canvas.setAttribute('width', canvasWidth);
104 | canvas.setAttribute('height', canvasHeight);
105 | return new Promise(async (resolve, reject) => {
106 | if (ctx) {
107 | ctx.translate(canvasOffsetLeft * ratio, canvasOffsetTop * ratio);
108 | ctx.rotate((Math.PI / 180) * Number(rotate));
109 | const markWidth = width * ratio;
110 | const markHeight = height * ratio;
111 | if (image) {
112 | const img = new Image();
113 | img.crossOrigin = 'anonymous';
114 | img.referrerPolicy = 'no-referrer';
115 | img.src = image;
116 | img.onload = async () => {
117 | ctx.drawImage(img, 0, 0, markWidth, markHeight);
118 | return resolve(canvas.toDataURL());
119 | };
120 | img.onerror = (error) => {
121 | return reject(error);
122 | };
123 | } else if (content) {
124 | const markSize = Number(fontSize) * ratio;
125 | ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
126 | ctx.fillStyle = fontColor;
127 | if (Array.isArray(content)) {
128 | content?.forEach((item: string, index: number) => ctx.fillText(item, 0, index * 50));
129 | } else {
130 | ctx.fillText(content, 0, 0);
131 | }
132 | return resolve(canvas.toDataURL());
133 | }
134 | } else {
135 | return reject('Error: Canvas is not supported in the current environment');
136 | }
137 | });
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "module": "es2015",
5 | "target": "es2017",
6 | "esModuleInterop": true,
7 | "declaration": true,
8 | "noImplicitAny": true,
9 | "resolveJsonModule": true,
10 | "moduleResolution": "node",
11 | "emitDecoratorMetadata": true,
12 | "experimentalDecorators": true,
13 | "strict": false,
14 | "skipLibCheck": true,
15 | "outDir": "lib",
16 | "baseUrl": "."
17 | },
18 | "include": ["src"],
19 | "exclude": ["dist"]
20 | }
21 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.1",
3 | "packages": ["website", "core", "react"]
4 | }
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "⬇️⬇️⬇️⬇️⬇️ package ⬇️⬇️⬇️⬇️⬇️": "▼▼▼▼▼ package ▼▼▼▼▼",
5 | "build": "lerna exec --scope @uiw/* -- npm run build",
6 | "watch": "lerna exec --scope @uiw/react-watermark -- npm run start",
7 | "watch:core": "lerna exec --scope @uiw/watermark.js -- npm start",
8 | "bundle": "lerna exec --scope @uiw/* -- ncc build src/index.tsx --target web --filename dist",
9 | "bundle:min": "lerna exec --scope @uiw/* -- ncc build src/index.tsx --target web --filename dist --minify",
10 | "⬆️⬆️⬆️⬆️⬆️ package ⬆️⬆️⬆️⬆️⬆️": "▲▲▲▲▲ package ▲▲▲▲▲",
11 | "start": "lerna exec --scope website -- npm run start",
12 | "doc": "lerna exec --scope website -- npm run build",
13 | "bootstrap": "lerna bootstrap",
14 | "hoist": "lerna bootstrap --hoist",
15 | "test": "tsbb test",
16 | "coverage": "tsbb test --coverage --bail",
17 | "prepare": "husky install",
18 | "version": "lerna version --exact --force-publish --no-push --no-git-tag-version",
19 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
20 | "remove": "npm run clean && lerna exec \"rm -rf package-lock.json\" --scope @uiw/* --scope website",
21 | "clean": "lerna clean --yes"
22 | },
23 | "lint-staged": {
24 | "*.{js,jsx,ts,tsx,less,md,json}": [
25 | "prettier --write"
26 | ]
27 | },
28 | "jest": {
29 | "collectCoverageFrom": [
30 | "/react/src/*.{tsx,ts}",
31 | "!**/*.{js,d.ts}"
32 | ],
33 | "testMatch": [
34 | "/test/*.{ts,tsx}",
35 | "/packages/**/__tests__/*.{ts,tsx}"
36 | ],
37 | "transformIgnorePatterns": [
38 | "/node_modules/?!(.*)"
39 | ]
40 | },
41 | "workspaces": [
42 | "website",
43 | "react",
44 | "core"
45 | ],
46 | "devDependencies": {
47 | "@kkt/ncc": "^1.0.14",
48 | "@types/react-test-renderer": "^18.0.0",
49 | "compile-less-cli": "^1.8.13",
50 | "husky": "^8.0.1",
51 | "jest": "^29.5.0",
52 | "jest-canvas-mock": "^2.5.0",
53 | "jest-watch-typeahead": "^2.2.2",
54 | "jest-environment-jsdom": "^29.5.0",
55 | "lerna": "^7.1.1",
56 | "lint-staged": "^13.0.3",
57 | "prettier": "^2.7.1",
58 | "pretty-quick": "^3.1.3",
59 | "react-test-renderer": "^18.2.0",
60 | "tsbb": "^4.1.14"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/react/.kktrc.ts:
--------------------------------------------------------------------------------
1 | import { LoaderConfOptions, WebpackConfiguration } from 'kkt';
2 | import lessModules from '@kkt/less-modules';
3 |
4 | export default (conf: WebpackConfiguration, env: 'development' | 'production', options: LoaderConfOptions) => {
5 | conf = lessModules(conf, env, options);
6 | if (options.bundle) {
7 | conf.output!.library = '@uiw/react-watermark';
8 | conf.externals = {
9 | react: {
10 | root: 'React',
11 | commonjs2: 'react',
12 | commonjs: 'react',
13 | amd: 'react',
14 | },
15 | };
16 | }
17 | return conf;
18 | };
19 |
--------------------------------------------------------------------------------
/react/README.md:
--------------------------------------------------------------------------------
1 |
2 | react-watermark
3 | ===
4 |
5 |
6 | [](https://jaywcjlove.github.io/#/sponsor)
7 | [](https://github.com/uiwjs/react-watermark/actions/workflows/ci.yml)
8 | [](https://uiwjs.github.io/react-watermark/coverage/lcov-report/)
9 | [](https://www.npmjs.com/package/@uiw/react-watermark)
10 | [](https://www.npmjs.com/package/@uiw/react-watermark)
11 |
12 | A react component that adds a watermark to an area of a web page. Example Preview: https://uiwjs.github.io/react-watermark
13 |
14 | ## Install
15 |
16 | Not dependent on **uiw**.
17 |
18 | ```bash
19 | npm i @uiw/react-watermark
20 | # Or
21 | npm i @uiw/watermark.js
22 | ```
23 |
24 | ### Using
25 |
26 | ```jsx mdx:preview
27 | import React from "react";
28 | import Watermark from '@uiw/react-watermark';
29 |
30 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
31 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
32 |
33 | Declarative views make your code more predictable and easier to debug.`;
34 |
35 | export default function App() {
36 | return (
37 |
41 |
42 |
43 | );
44 | }
45 | ```
46 |
47 | Multi-line watermark text
48 |
49 | ```jsx mdx:preview
50 | import React from "react";
51 | import Watermark from '@uiw/react-watermark';
52 |
53 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
54 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
55 |
56 | Declarative views make your code more predictable and easier to debug.`;
57 |
58 | export default function App() {
59 | return (
60 |
64 |
65 |
66 | );
67 | }
68 | ```
69 |
70 | More props settings
71 |
72 | ```jsx mdx:preview
73 | import React from "react";
74 | import Watermark from '@uiw/react-watermark';
75 |
76 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
77 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
78 |
79 | Declarative views make your code more predictable and easier to debug.`;
80 |
81 | export default function App() {
82 | return (
83 |
95 |
96 |
97 | );
98 | }
99 | ```
100 |
101 | Image watermark
102 |
103 | ```jsx mdx:preview
104 | import React from "react";
105 | import Watermark from '@uiw/react-watermark';
106 |
107 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
108 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
109 |
110 | Declarative views make your code more predictable and easier to debug.
111 |
112 | Build encapsulated components that manage their own state, then compose them to make complex UIs.
113 |
114 | Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.`;
115 |
116 | export default function App() {
117 | return (
118 |
123 |
124 |
125 | );
126 | }
127 | ```
128 |
129 | Image base64 watermark
130 |
131 | ```jsx mdx:preview
132 | import React from "react";
133 | import Watermark from '@uiw/react-watermark';
134 |
135 | const imageBase64 = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNjUiIGhlaWdodD0iMzEiIHZpZXdCb3g9IjAgMCAxNjUgMzEiPjxnIGZpbGw9IiNBQUEiIG9wYWNpdHk9Ii40MjEiPjxwYXRoIGQ9Ik02Ljg0NDg5MzY2LDAgTDEzLjY4OTc4NzMsNS4yOTg0MzczNCBMMTEuMDc1MjcwNiwxMy44NzE0ODkgTDIuNjE0NTE2NzMsMTMuODcxNDg5IEwwLDUuMjk4NDM3MzQgTDYuODQ0ODkzNjYsMCBaIE02Ljg0NDg5MzY2LDMxIEwwLDI1LjcwMTU2MjYgTDIuNjE0NTE2NzMsMTcuMTI4NTEwOSBMMTEuMDc1MjcwNiwxNy4xMjg1MTA5IEwxMy42ODk3ODczLDI1LjcwMTU2MjYgTDYuODQ0ODkzNjYsMzEgWiBNMjYsMjAuMTAwMjMwOSBMMTcuOTUzMjE4MSwyMi44ODU1OTgzIEwxMi45Nzk5MDY0LDE1LjU5Mjk5NiBMMTcuOTUzMDEyNyw4LjMwMDU1MjU0IEwyNiwxMS4wODYxNzcgTDI2LDIwLjEwMDIzMDkgWiIvPjxwb2x5Z29uIHBvaW50cz0iMzQgNiAzOS4wMjcgMjMgNDEuODk2IDIzIDQ1LjQ1MSA5Ljg4MSA0NS41NDkgOS44ODEgNDkuMTA0IDIzIDUxLjk0OSAyMyA1NyA2IDUzLjk4NCA2IDUwLjU3NiAxOS4yMzggNTAuNDc4IDE5LjIzOCA0Ni44OTggNiA0NC4xMDIgNiA0MC41MjIgMTkuMjM4IDQwLjQyNCAxOS4yMzggMzcuMDE2IDYiLz48cGF0aCBkPSJNNjQuNDg3MTc5NSAxMUM2Mi44NDYxNTM4IDExIDYxLjUxMjgyMDUgMTEuMjgyNTY4OCA2MC41Mzg0NjE1IDExLjg5OTA4MjYgNTkuNDEwMjU2NCAxMi41NjY5NzI1IDU4LjY5MjMwNzcgMTMuNjQ1ODcxNiA1OC40MTAyNTY0IDE1LjA4NDQwMzdMNjEuMTAyNTY0MSAxNS4zMTU1OTYzQzYxLjI1NjQxMDMgMTQuNTcwNjQyMiA2MS42NDEwMjU2IDE0LjAzMTE5MjcgNjIuMjU2NDEwMyAxMy42NzE1NTk2IDYyLjc2OTIzMDggMTMuMzYzMzAyOCA2My40NjE1Mzg1IDEzLjIwOTE3NDMgNjQuMzA3NjkyMyAxMy4yMDkxNzQzIDY2LjMwNzY5MjMgMTMuMjA5MTc0MyA2Ny4zMDc2OTIzIDE0LjEzMzk0NSA2Ny4zMDc2OTIzIDE1Ljk4MzQ4NjJMNjcuMzA3NjkyMyAxNi41MjI5MzU4IDY0LjMzMzMzMzMgMTYuNkM2Mi4zODQ2MTU0IDE2LjY1MTM3NjEgNjAuODQ2MTUzOCAxNy4wMzY2OTcyIDU5Ljc2OTIzMDggMTcuODA3MzM5NCA1OC41ODk3NDM2IDE4LjYwMzY2OTcgNTggMTkuNzU5NjMzIDU4IDIxLjI0OTU0MTMgNTggMjIuMzU0MTI4NCA1OC40MTAyNTY0IDIzLjI1MzIxMSA1OS4yNTY0MTAzIDIzLjk0Njc4OSA2MC4wMjU2NDEgMjQuNjQwMzY3IDYxLjEwMjU2NDEgMjUgNjIuNDg3MTc5NSAyNSA2My42NjY2NjY3IDI1IDY0LjY5MjMwNzcgMjQuNzY4ODA3MyA2NS41NjQxMDI2IDI0LjM1Nzc5ODIgNjYuMzMzMzMzMyAyMy45NzI0NzcxIDY2Ljk3NDM1OSAyMy40MzMwMjc1IDY3LjQ4NzE3OTUgMjIuNzY1MTM3Nkw2Ny40ODcxNzk1IDI0LjY0MDM2NyA3MCAyNC42NDAzNjcgNzAgMTYuMTYzMzAyOEM3MCAxNC41NDQ5NTQxIDY5LjU4OTc0MzYgMTMuMzExOTI2NiA2OC43OTQ4NzE4IDEyLjQ2NDIyMDIgNjcuODcxNzk0OSAxMS40ODgwNzM0IDY2LjQzNTg5NzQgMTEgNjQuNDg3MTc5NSAxMVpNNjcuMzA3NjkyMyAxOC41MDA5MTc0IDY3LjMwNzY5MjMgMTkuMjcxNTU5NkM2Ny4zMDc2OTIzIDIwLjI5OTA4MjYgNjYuODcxNzk0OSAyMS4xNzI0NzcxIDY2LjA1MTI4MjEgMjEuODY2MDU1IDY1LjIzMDc2OTIgMjIuNTU5NjMzIDY0LjI1NjQxMDMgMjIuOTE5MjY2MSA2My4xMDI1NjQxIDIyLjkxOTI2NjEgNjIuNDEwMjU2NCAyMi45MTkyNjYxIDYxLjg0NjE1MzggMjIuNzM5NDQ5NSA2MS40MzU4OTc0IDIyLjQwNTUwNDYgNjEgMjIuMDcxNTU5NiA2MC43OTQ4NzE4IDIxLjY2MDU1MDUgNjAuNzk0ODcxOCAyMS4xNDY3ODkgNjAuNzk0ODcxOCAxOS41MDI3NTIzIDYyLjAyNTY0MSAxOC42MjkzNTc4IDY0LjUxMjgyMDUgMTguNTc3OTgxN0w2Ny4zMDc2OTIzIDE4LjUwMDkxNzRaTTc4LjA1IDggNzUuNCA5LjA5MzU2NzI1IDc1LjQgMTIuMTUwNTg0OCA3MyAxMi4xNTA1ODQ4IDczIDE0LjMzNzcxOTMgNzUuNCAxNC4zMzc3MTkzIDc1LjQgMjEuNzkzODU5NkM3NS40IDIyLjgxMjg2NTUgNzUuNjI1IDIzLjU4MzMzMzMgNzYuMTI1IDI0LjEzMDExNyA3Ni42MjUgMjQuNzAxNzU0NCA3Ny40NSAyNSA3OC41NSAyNUw4MC42NzUgMjUgODAuNjc1IDIyLjgxMjg2NTUgNzguOTc1IDIyLjgxMjg2NTVDNzguNjUgMjIuODEyODY1NSA3OC40MjUgMjIuNzEzNDUwMyA3OC4yNzUgMjIuNTY0MzI3NSA3OC4xMjUgMjIuMzkwMzUwOSA3OC4wNSAyMi4xNDE4MTI5IDc4LjA1IDIxLjc5Mzg1OTZMNzguMDUgMTQuMzM3NzE5MyA4MSAxNC4zMzc3MTkzIDgxIDEyLjE1MDU4NDggNzguMDUgMTIuMTUwNTg0OCA3OC4wNSA4Wk04OC45MTY2NjY3IDExQzg2LjgwNTU1NTYgMTEgODUuMTExMTExMSAxMS42Njc4ODk5IDgzLjg4ODg4ODkgMTMuMDI5MzU3OCA4Mi42MTExMTExIDE0LjM2NTEzNzYgODIgMTYuMDA5MTc0MyA4MiAxNy45ODcxNTYgODIgMjAuMTcwNjQyMiA4Mi42NjY2NjY3IDIxLjg5MTc0MzEgODQgMjMuMTc2MTQ2OCA4NS4yMjIyMjIyIDI0LjM4MzQ4NjIgODYuOTE2NjY2NyAyNSA4OS4wMjc3Nzc4IDI1IDkwLjk0NDQ0NDQgMjUgOTIuNTI3Nzc3OCAyNC40ODYyMzg1IDkzLjc3Nzc3NzggMjMuNDg0NDAzNyA5NC43Nzc3Nzc4IDIyLjY2MjM4NTMgOTUuNDE2NjY2NyAyMS42MDkxNzQzIDk1Ljc1IDIwLjM3NjE0NjhMOTIuODA1NTU1NiAyMC4zNzYxNDY4QzkyLjQxNjY2NjcgMjEuMTQ2Nzg5IDkyIDIxLjczNzYxNDcgOTEuNSAyMi4wOTcyNDc3IDkwLjg2MTExMTEgMjIuNTMzOTQ1IDkwLjAyNzc3NzggMjIuNzY1MTM3NiA4OSAyMi43NjUxMzc2IDg3LjgzMzMzMzMgMjIuNzY1MTM3NiA4Ni45MTY2NjY3IDIyLjQwNTUwNDYgODYuMjUgMjEuNzM3NjE0NyA4NS41ODMzMzMzIDIxLjA0NDAzNjcgODUuMjIyMjIyMiAyMC4wNDIyMDE4IDg1LjExMTExMTEgMTguNzU3Nzk4Mkw5NiAxOC43NTc3OTgyQzk1Ljk3MjIyMjIgMTYuMzk0NDk1NCA5NS4zODg4ODg5IDE0LjU0NDk1NDEgOTQuMjc3Nzc3OCAxMy4yMDkxNzQzIDkzLjA1NTU1NTYgMTEuNzE5MjY2MSA5MS4yNSAxMSA4OC45MTY2NjY3IDExWk04OSAxMy4yMzQ4NjI0QzkxLjMzMzMzMzMgMTMuMjM0ODYyNCA5Mi42Mzg4ODg5IDE0LjM5MDgyNTcgOTIuOTE2NjY2NyAxNi43MDI3NTIzTDg1LjE2NjY2NjcgMTYuNzAyNzUyM0M4NS4zMzMzMzMzIDE1LjU3MjQ3NzEgODUuNzUgMTQuNzI0NzcwNiA4Ni4zNjExMTExIDE0LjEzMzk0NSA4Ny4wMjc3Nzc4IDEzLjUxNzQzMTIgODcuODg4ODg4OSAxMy4yMzQ4NjI0IDg5IDEzLjIzNDg2MjRaTTEwMi4yNTQwNzIgOUMxMDEuNDIwMTk1IDkgMTAwLjY5MDU1NCA5LjIzNzI4ODE0IDEwMC4wNjUxNDcgOS43NjQ1OTUxIDk5LjU0Mzk3MzkgMTAuMTMzNzEgOTkuMTAwOTc3MiAxMC42ODczODIzIDk4Ljc2MjIxNSAxMS40MjU2MTIxTDk4Ljc2MjIxNSA5LjM2OTExNDg4IDk2IDkuMzY5MTE0ODggOTYgMjMgOTguNzYyMjE1IDIzIDk4Ljc2MjIxNSAxNS43NzU4OTQ1Qzk4Ljc2MjIxNSAxNC41ODk0NTM5IDk5LjEwMDk3NzIgMTMuNjEzOTM2IDk5LjgwNDU2MDMgMTIuODc1NzA2MiAxMDAuNDU2MDI2IDEyLjE5MDIwNzIgMTAxLjIxMTcyNiAxMS44NDc0NTc2IDEwMi4wNDU2MDMgMTEuODQ3NDU3NiAxMDIuNjcxMDEgMTEuODQ3NDU3NiAxMDMuMzIyNDc2IDExLjkyNjU1MzcgMTA0IDEyLjEzNzQ3NjVMMTA0IDkuMzQyNzQ5NTNDMTAzLjUzMDk0NSA5LjEwNTQ2MTM5IDEwMi45MzE1OTYgOSAxMDIuMjU0MDcyIDlaTTExMS4yMjQ1NzEgOUMxMDkuOTE5NDE5IDkgMTA4LjcxNDY2MyA5LjYwNjQwMzAxIDEwNy42NjA1MDIgMTAuODcxOTM5N0wxMDcuNjYwNTAyIDkuMzY5MTE0ODggMTA1IDkuMzY5MTE0ODggMTA1IDIzIDEwNy42NjA1MDIgMjMgMTA3LjY2MDUwMiAxNC44MDAzNzY2QzEwNy42NjA1MDIgMTMuODUxMjI0MSAxMDcuOTExNDkzIDEzLjAzMzg5ODMgMTA4LjQ2MzY3MiAxMi4zNzQ3NjQ2IDEwOC45NjU2NTQgMTEuNjg5MjY1NSAxMDkuNjY4NDI4IDExLjM0NjUxNiAxMTAuNTIxNzk3IDExLjM0NjUxNiAxMTIuMjc4NzMyIDExLjM0NjUxNiAxMTMuMTgyMjk5IDEyLjQwMTEyOTkgMTEzLjE4MjI5OSAxNC41NjMwODg1TDExMy4xODIyOTkgMjMgMTE1Ljg0MjgwMSAyMyAxMTUuODQyODAxIDE0LjYxNTgxOTJDMTE1Ljg0MjgwMSAxMy42MTM5MzYgMTE2LjA5Mzc5MSAxMi43OTY2MTAyIDExNi41OTU3NzMgMTIuMjE2NTcyNSAxMTcuMDk3NzU0IDExLjYzNjUzNDggMTE3LjcwMDEzMiAxMS4zNDY1MTYgMTE4LjQ1MzEwNCAxMS4zNDY1MTYgMTE5LjQ1NzA2NyAxMS4zNDY1MTYgMTIwLjIxMDA0IDExLjU4MzgwNDEgMTIwLjY2MTgyMyAxMi4xMTExMTExIDEyMS4xMTM2MDYgMTIuNjEyMDUyNyAxMjEuMzM5NDk4IDEzLjQyOTM3ODUgMTIxLjMzOTQ5OCAxNC41MzY3MjMyTDEyMS4zMzk0OTggMjMgMTI0IDIzIDEyNCAxNC4wNjIxNDY5QzEyNCAxMi41MzI5NTY3IDEyMy41NDgyMTcgMTEuMzIwMTUwNyAxMjIuNjk0ODQ4IDEwLjM5NzM2MzUgMTIxLjgxNjM4IDkuNDQ4MjEwOTIgMTIwLjcxMjAyMSA5IDExOS4zODE3NyA5IDExOC41MDMzMDMgOSAxMTcuNzc1NDI5IDkuMTU4MTkyMDkgMTE3LjE3MzA1MiA5LjUwMDk0MTYyIDExNi41MjA0NzYgOS44NDM2OTExNSAxMTUuODkyOTk5IDEwLjQ1MDA5NDIgMTE1LjMxNTcyIDExLjI5Mzc4NTMgMTE0LjUxMjU1IDkuNzY0NTk1MSAxMTMuMTMyMSA5IDExMS4yMjQ1NzEgOVpNMTMzLjQ4NzE3OSAxMUMxMzEuODQ2MTU0IDExIDEzMC41MTI4MjEgMTEuMjgyNTY4OCAxMjkuNTM4NDYyIDExLjg5OTA4MjYgMTI4LjQxMDI1NiAxMi41NjY5NzI1IDEyNy42OTIzMDggMTMuNjQ1ODcxNiAxMjcuNDEwMjU2IDE1LjA4NDQwMzdMMTMwLjEwMjU2NCAxNS4zMTU1OTYzQzEzMC4yNTY0MSAxNC41NzA2NDIyIDEzMC42NDEwMjYgMTQuMDMxMTkyNyAxMzEuMjU2NDEgMTMuNjcxNTU5NiAxMzEuNzY5MjMxIDEzLjM2MzMwMjggMTMyLjQ2MTUzOCAxMy4yMDkxNzQzIDEzMy4zMDc2OTIgMTMuMjA5MTc0MyAxMzUuMzA3NjkyIDEzLjIwOTE3NDMgMTM2LjMwNzY5MiAxNC4xMzM5NDUgMTM2LjMwNzY5MiAxNS45ODM0ODYyTDEzNi4zMDc2OTIgMTYuNTIyOTM1OCAxMzMuMzMzMzMzIDE2LjZDMTMxLjM4NDYxNSAxNi42NTEzNzYxIDEyOS44NDYxNTQgMTcuMDM2Njk3MiAxMjguNzY5MjMxIDE3LjgwNzMzOTQgMTI3LjU4OTc0NCAxOC42MDM2Njk3IDEyNyAxOS43NTk2MzMgMTI3IDIxLjI0OTU0MTMgMTI3IDIyLjM1NDEyODQgMTI3LjQxMDI1NiAyMy4yNTMyMTEgMTI4LjI1NjQxIDIzLjk0Njc4OSAxMjkuMDI1NjQxIDI0LjY0MDM2NyAxMzAuMTAyNTY0IDI1IDEzMS40ODcxNzkgMjUgMTMyLjY2NjY2NyAyNSAxMzMuNjkyMzA4IDI0Ljc2ODgwNzMgMTM0LjU2NDEwMyAyNC4zNTc3OTgyIDEzNS4zMzMzMzMgMjMuOTcyNDc3MSAxMzUuOTc0MzU5IDIzLjQzMzAyNzUgMTM2LjQ4NzE3OSAyMi43NjUxMzc2TDEzNi40ODcxNzkgMjQuNjQwMzY3IDEzOSAyNC42NDAzNjcgMTM5IDE2LjE2MzMwMjhDMTM5IDE0LjU0NDk1NDEgMTM4LjU4OTc0NCAxMy4zMTE5MjY2IDEzNy43OTQ4NzIgMTIuNDY0MjIwMiAxMzYuODcxNzk1IDExLjQ4ODA3MzQgMTM1LjQzNTg5NyAxMSAxMzMuNDg3MTc5IDExWk0xMzYuMzA3NjkyIDE4LjUwMDkxNzQgMTM2LjMwNzY5MiAxOS4yNzE1NTk2QzEzNi4zMDc2OTIgMjAuMjk5MDgyNiAxMzUuODcxNzk1IDIxLjE3MjQ3NzEgMTM1LjA1MTI4MiAyMS44NjYwNTUgMTM0LjIzMDc2OSAyMi41NTk2MzMgMTMzLjI1NjQxIDIyLjkxOTI2NjEgMTMyLjEwMjU2NCAyMi45MTkyNjYxIDEzMS40MTAyNTYgMjIuOTE5MjY2MSAxMzAuODQ2MTU0IDIyLjczOTQ0OTUgMTMwLjQzNTg5NyAyMi40MDU1MDQ2IDEzMCAyMi4wNzE1NTk2IDEyOS43OTQ4NzIgMjEuNjYwNTUwNSAxMjkuNzk0ODcyIDIxLjE0Njc4OSAxMjkuNzk0ODcyIDE5LjUwMjc1MjMgMTMxLjAyNTY0MSAxOC42MjkzNTc4IDEzMy41MTI4MjEgMTguNTc3OTgxN0wxMzYuMzA3NjkyIDE4LjUwMDkxNzRaTTE0OC4yNTQwNzIgOUMxNDcuNDIwMTk1IDkgMTQ2LjY5MDU1NCA5LjIzNzI4ODE0IDE0Ni4wNjUxNDcgOS43NjQ1OTUxIDE0NS41NDM5NzQgMTAuMTMzNzEgMTQ1LjEwMDk3NyAxMC42ODczODIzIDE0NC43NjIyMTUgMTEuNDI1NjEyMUwxNDQuNzYyMjE1IDkuMzY5MTE0ODggMTQyIDkuMzY5MTE0ODggMTQyIDIzIDE0NC43NjIyMTUgMjMgMTQ0Ljc2MjIxNSAxNS43NzU4OTQ1QzE0NC43NjIyMTUgMTQuNTg5NDUzOSAxNDUuMTAwOTc3IDEzLjYxMzkzNiAxNDUuODA0NTYgMTIuODc1NzA2MiAxNDYuNDU2MDI2IDEyLjE5MDIwNzIgMTQ3LjIxMTcyNiAxMS44NDc0NTc2IDE0OC4wNDU2MDMgMTEuODQ3NDU3NiAxNDguNjcxMDEgMTEuODQ3NDU3NiAxNDkuMzIyNDc2IDExLjkyNjU1MzcgMTUwIDEyLjEzNzQ3NjVMMTUwIDkuMzQyNzQ5NTNDMTQ5LjUzMDk0NSA5LjEwNTQ2MTM5IDE0OC45MzE1OTYgOSAxNDguMjU0MDcyIDlaIi8+PHBvbHlnb24gcG9pbnRzPSIxNTAgNiAxNTAgMjUgMTUzLjA0NyAyNSAxNTMuMDQ3IDIwLjI3NiAxNTQuNzQzIDE4Ljg0MSAxNjAuMDkgMjUgMTY0IDI1IDE1Ni44NDIgMTcuMDY2IDE2My40MjUgMTEuNTA3IDE1OS40ODcgMTEuNTA3IDE1My4wNDcgMTcuMDY2IDE1My4wNDcgNiIvPjwvZz48L3N2Zz4='
136 |
137 | const style = { width: '100%', maxWidth: '100%', height: 200, display: 'block' };
138 | const text = `React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
139 |
140 | Declarative views make your code more predictable and easier to debug.
141 |
142 | Build encapsulated components that manage their own state, then compose them to make complex UIs.
143 |
144 | Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.`;
145 |
146 | export default function App() {
147 | return (
148 |
153 |
154 |
155 | );
156 | }
157 | ```
158 |
159 | ## In the static pages
160 |
161 | [](https://www.npmjs.com/package/@uiw/watermark.js)
162 | [](https://www.npmjs.com/package/@uiw/watermark.js)
163 |
164 | ```bash
165 | npm i @uiw/watermark.js
166 | ```
167 |
168 | ```js
169 | import Watermark from '@uiw/watermark.js';
170 |
171 | const watermark = new Watermark({
172 | content: 'Hello Watermark!'
173 | });
174 | watermark.create().then((mark) => {
175 | console.log('output:', mark)
176 | })
177 | .catch((err) => {
178 | console.log(err, 'err')
179 | })
180 | ```
181 |
182 | Or manually download and link `watermark.js` in your HTML, It can also be downloaded via [UNPKG](https://unpkg.com/browse/@uiw/watermark.js/):
183 |
184 | CDN: [UNPKG](https://unpkg.com/@uiw/watermark.js/dist/) | [jsDelivr](https://cdn.jsdelivr.net/npm/@uiw/watermark.js/) | [Githack](https://raw.githack.com/uiwjs/watermark.js/gh-pages/watermark.min.js) | [Statically](https://cdn.statically.io/gh/uiwjs/watermark.js/gh-pages/watermark.min.js)
185 |
186 | ```html
187 |
200 |
201 |
202 |
203 |
215 |
216 | ```
217 |
218 | ## API
219 |
220 | ```ts
221 | import React from 'react';
222 | import { WatermarkOptions } from '@uiw/watermark.js';
223 | export interface WatermarkProps extends Omit, 'width' | 'height'>, WatermarkOptions {
224 | prefixCls?: string;
225 | /** watermark style */
226 | markStyle?: React.CSSProperties;
227 | /** watermark class name */
228 | markClassName?: string;
229 | /** watermark text content */
230 | text?: string;
231 | }
232 | declare const Watermark: React.ForwardRefExoticComponent>;
233 | export default Watermark;
234 | ```
235 |
236 | **`@uiw/watermark.js`**
237 |
238 | ```ts
239 | export interface WatermarkOptions {
240 | /** watermark text content */
241 | content?: string | string[];
242 | /**
243 | * When the watermark is drawn, the rotation angle, in `°`. @default `-22`
244 | */
245 | rotate?: number;
246 | /**
247 | * High-definition print image source, for high-definition screen display,
248 | * it is recommended to use 2x or 3x image, and priority to use image rendering watermark.
249 | */
250 | image?: string;
251 | /** Horizontal spacing between watermarks. @default `212` */
252 | gapX?: number;
253 | /** vertical spacing between watermarks. @default `222` */
254 | gapY?: number;
255 | /** width of watermark. @default `120` */
256 | width?: number;
257 | /** height of watermark @default `64` */
258 | height?: number;
259 | /**
260 | * The vertical offset of the watermark drawn on the canvas.
261 | * Normally, the watermark is drawn in the middle position, ie `offsetTop = gapY / 2`
262 | */
263 | offsetLeft?: number;
264 | /**
265 | * The horizontal offset of the watermark drawn on the canvas, under normal circumstances,
266 | * the watermark is drawn in the middle position, ie `offsetTop = gapX / 2`
267 | */
268 | offsetTop?: number;
269 | /** text size @default `16` */
270 | fontSize?: number;
271 | /** text family @default `sans-serif` */
272 | fontFamily?: string;
273 | /** text weight @default `normal` */
274 | fontWeight?: 'normal' | 'light' | 'weight' | number;
275 | /** text color @default `rgba(0,0,0,.15)` */
276 | fontColor?: string;
277 | /** text style */
278 | fontStyle?: CanvasFillStrokeStyles['fillStyle'];
279 | }
280 | export default class Watermark {
281 | option: WatermarkOptions;
282 | constructor(options: WatermarkOptions);
283 | create(): Promise;
284 | }
285 | ```
286 |
287 | ## Development
288 |
289 | 1. Install
290 |
291 | Dependencies in the installation package and website
292 |
293 | ```bash
294 | npm install
295 | ```
296 |
297 | 2. To develop, run the self-reloading build:
298 |
299 | ```bash
300 | npm run build # Compile packages 📦 @uiw/react-watermark & @uiw/watermark.js
301 | npm run watch # Real-time compilation 📦 @uiw/react-watermark
302 | npm run watch:core # Real-time compilation 📦 @uiw/watermark.js
303 | ```
304 |
305 | 3. Run Document Website Environment:
306 |
307 | ```bash
308 | npm run start
309 | ```
310 |
311 | 4. To contribute, please fork repos, add your patch and tests for it (in the `test/` folder) and submit a pull request.
312 |
313 | ```
314 | npm run test
315 | ```
316 |
317 |
318 | ## Contributors
319 |
320 | As always, thanks to our amazing contributors!
321 |
322 |
323 |
324 |
325 |
326 | Made with [action-contributors](https://github.com/jaywcjlove/github-action-contributors).
327 |
328 | ## License
329 |
330 | Licensed under the MIT License.
--------------------------------------------------------------------------------
/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uiw/react-watermark",
3 | "version": "1.0.1",
4 | "description": "React Monorepo Template.",
5 | "author": "Kenny Wong ",
6 | "homepage": "https://uiwjs.github.io/react-watermark",
7 | "funding": "https://jaywcjlove.github.io/#/sponsor",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/uiwjs/react-watermark.git"
11 | },
12 | "license": "MIT",
13 | "main": "./cjs/index.js",
14 | "module": "./esm/index.js",
15 | "scripts": {
16 | "watch": "tsbb watch src/index.tsx --use-babel --cjs cjs",
17 | "build": "tsbb build src/index.tsx --use-babel --cjs cjs --bail"
18 | },
19 | "files": [
20 | "dist.css",
21 | "dist",
22 | "cjs",
23 | "esm",
24 | "src"
25 | ],
26 | "publishConfig": {
27 | "access": "public"
28 | },
29 | "keywords": [
30 | "react-watermark",
31 | "react.js",
32 | "react",
33 | "template",
34 | "monorepo",
35 | "watermark",
36 | "uiw",
37 | "uiw-react",
38 | "react-component",
39 | "component",
40 | "components",
41 | "ui",
42 | "css",
43 | "uikit",
44 | "react-ui",
45 | "framework"
46 | ],
47 | "peerDependencies": {
48 | "react": ">=16.9.0",
49 | "react-dom": ">=16.9.0"
50 | },
51 | "dependencies": {
52 | "@uiw/watermark.js": "1.0.1"
53 | },
54 | "devDependencies": {
55 | "@babel/runtime": "^7.18.9",
56 | "@types/react": "^18.0.17",
57 | "@types/react-dom": "^18.0.6",
58 | "react": "^18.2.0",
59 | "react-dom": "^18.2.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/react/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { CSSProperties, ForwardedRef, forwardRef, useEffect, useState } from 'react';
2 | import WatermarkJS, { WatermarkOptions } from '@uiw/watermark.js';
3 |
4 | export interface WatermarkProps
5 | extends Omit, 'width' | 'height' | 'content'>,
6 | WatermarkOptions {
7 | prefixCls?: string;
8 | /** watermark style */
9 | markStyle?: React.CSSProperties;
10 | /** watermark class name */
11 | markClassName?: string;
12 | /** watermark text content */
13 | text?: string;
14 | }
15 |
16 | function FancyWatermark(props: WatermarkProps, ref: ForwardedRef) {
17 | const {
18 | prefixCls = 'w-watermark',
19 | text,
20 | className,
21 | markClassName,
22 | markStyle,
23 | content,
24 | rotate,
25 | image,
26 | gapX = 212,
27 | gapY,
28 | width = 120,
29 | height,
30 | offsetLeft,
31 | offsetTop,
32 | fontSize,
33 | fontFamily,
34 | fontWeight,
35 | fontColor,
36 | fontStyle,
37 | ...other
38 | } = props;
39 | const style: CSSProperties = {
40 | ...props.style,
41 | position: 'relative',
42 | };
43 | const wrapperCls = [`${prefixCls}-wrapper`, className].filter(Boolean).join(' ');
44 | const waterMakrCls = [prefixCls, markClassName].filter(Boolean).join(' ');
45 | const [base64Url, setBase64Url] = useState('');
46 |
47 | useEffect(() => {
48 | const water = new WatermarkJS({
49 | content,
50 | rotate,
51 | image,
52 | gapX,
53 | gapY,
54 | width,
55 | height,
56 | offsetLeft,
57 | offsetTop,
58 | fontSize,
59 | fontFamily,
60 | fontWeight,
61 | fontColor,
62 | fontStyle,
63 | });
64 | water
65 | .create()
66 | .then((base64String) => setBase64Url(base64String))
67 | .catch(() => {});
68 | }, [
69 | content,
70 | rotate,
71 | image,
72 | gapX,
73 | gapY,
74 | width,
75 | height,
76 | offsetLeft,
77 | offsetTop,
78 | fontSize,
79 | fontFamily,
80 | fontWeight,
81 | fontColor,
82 | fontStyle,
83 | ]);
84 | const watermarkStyle: CSSProperties = {
85 | position: 'absolute',
86 | top: 0,
87 | left: 0,
88 | zIndex: 9,
89 | width: '100%',
90 | height: '100%',
91 | backgroundSize: `${gapX + width}px`,
92 | backgroundRepeat: 'repeat',
93 | ...markStyle,
94 | pointerEvents: 'none',
95 | };
96 | watermarkStyle.backgroundImage = `url(${base64Url})`;
97 | return (
98 |
99 | {props.children}
100 |
101 |
102 | );
103 | }
104 |
105 | const Watermark = forwardRef(FancyWatermark);
106 |
107 | export default Watermark;
108 |
--------------------------------------------------------------------------------
/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "outDir": "./cjs",
6 | "baseUrl": ".",
7 | "noEmit": false
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:base"],
4 | "packageRules": [
5 | {
6 | "matchPackagePatterns": ["*"],
7 | "rangeStrategy": "replace"
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/test/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment jsdom
3 | */
4 | import { render, screen } from '@testing-library/react';
5 | import renderer, { ReactTestRendererJSON } from 'react-test-renderer';
6 | import 'jest-canvas-mock';
7 | import '@testing-library/jest-dom';
8 | import React from 'react';
9 | import Watermark from '../react/src';
10 |
11 | test('renders learn react link', async () => {
12 | render(learn react);
13 | const linkElement = await screen.getByText(/learn react/i);
14 | expect(linkElement.getAttribute('class')).toEqual('w-watermark-wrapper')
15 | expect(linkElement.getAttribute('style')).toEqual('position: relative;')
16 | expect(linkElement).toBeInTheDocument();
17 | const component = renderer.create(learn react);
18 |
19 | let tree = component.toJSON() as ReactTestRendererJSON;
20 | const child = tree.children![1] as ReactTestRendererJSON;
21 | expect(child.props.className).toEqual('w-watermark');
22 | });
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "target": "esnext",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "esModuleInterop": true,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "declaration": true,
17 | "baseUrl": ".",
18 | "noFallthroughCasesInSwitch": true,
19 | "noEmit": true
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/website/.kktrc.ts:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import { LoaderConfOptions, WebpackConfiguration } from 'kkt';
3 | import { disableScopePlugin } from '@kkt/scope-plugin-options';
4 | import { mdCodeModulesLoader } from 'markdown-react-code-preview-loader';
5 | import pkg from './package.json';
6 |
7 | export default (conf: WebpackConfiguration, env: 'production' | 'development', options: LoaderConfOptions) => {
8 | conf = mdCodeModulesLoader(conf);
9 | conf = disableScopePlugin(conf);
10 | conf.plugins!.push(
11 | new webpack.DefinePlugin({
12 | VERSION: JSON.stringify(pkg.version),
13 | }),
14 | );
15 |
16 | conf.module!.exprContextCritical = false;
17 | /**
18 | * fix failed to parse source map issue
19 | * https://github.com/kktjs/kkt/issues/446
20 | */
21 | conf.ignoreWarnings = [{ module: /node_modules[\\/]parse5[\\/]/ }];
22 | if (env === 'production') {
23 | conf.output = { ...conf.output, publicPath: './' };
24 | conf.optimization = {
25 | ...conf.optimization,
26 | splitChunks: {
27 | cacheGroups: {
28 | reactvendor: {
29 | test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
30 | name: 'react-vendor',
31 | chunks: 'all',
32 | },
33 | refractor: {
34 | test: /[\\/]node_modules[\\/](refractor)[\\/]/,
35 | name: 'refractor-prismjs-vendor',
36 | chunks: 'all',
37 | },
38 | },
39 | },
40 | };
41 | }
42 |
43 | return conf;
44 | };
45 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | Document Website
2 | ===
3 |
4 | https://uiwjs.github.io/react-watermark
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "1.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "kkt start",
7 | "build": "kkt build"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "@uiw/react-markdown-preview-example": "^2.1.2",
12 | "@uiw/react-watermark": "1.0.1",
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@kkt/scope-plugin-options": "^7.5.2",
18 | "@types/react": "^18.0.17",
19 | "@types/react-dom": "^18.0.6",
20 | "kkt": "^7.5.2",
21 | "markdown-react-code-preview-loader": "^2.1.2"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/website/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uiwjs/react-watermark/ee628a0d1b8e96a52377438d5254a7d0715f9154/website/public/favicon.ico
--------------------------------------------------------------------------------
/website/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React Watermark
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/website/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 | import MarkdownPreviewExample from '@uiw/react-markdown-preview-example';
3 | import data from '@uiw/react-watermark/README.md';
4 | import Watermark from '@uiw/react-watermark';
5 |
6 | const Github = MarkdownPreviewExample.Github;
7 |
8 | const container = document.getElementById('root');
9 | const root = createRoot(container!);
10 | root.render(
11 |
17 |
21 | Sponsor
22 | ,
23 | ]}
24 | />
25 |
33 |
34 |
35 | ,
36 | );
37 |
--------------------------------------------------------------------------------
/website/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare var VERSION: string;
4 |
5 | declare module '*.md' {
6 | import { CodeBlockData } from 'markdown-react-code-preview-loader';
7 | const src: CodeBlockData;
8 | export default src;
9 | }
10 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig",
3 | "include": [".kktrc.ts", "src"],
4 | "compilerOptions": {
5 | "jsx": "react-jsx",
6 | "baseUrl": "./src",
7 | "noEmit": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------