For images stored in an Object Storage Service (OSS) bucket, you can use the blind watermarking feature to add and parse invisible text watermarks.
Overview
With blind watermarking, you can embed invisible text information into an image without affecting its visual quality. To retrieve the watermark content, you can use the blind watermark parsing operation. The watermark content can be parsed correctly even if the image is subjected to a certain degree of attacks, such as cropping, scaling, doodling, or color swapping.
Scenarios
Authentication and accountability: Determine whether your images have been stolen by malicious attackers.
Duplicate upload check: Determine whether an uploaded image already exists in a resource library.
Resource leak prevention: Obtain information about the source of a leak from the blind watermark embedded in a distributed image.
How to use
Add a blind watermark: Use the synchronous processing method (
x-oss-process). After the watermark is added, the image is saved as a new file.Parse a blind watermark: Use the asynchronous processing method (
x-oss-async-process). The parsed text watermark is included in the asynchronous message.
Prerequisites
The Intelligent Media Management (IMM) service is activated.
An IMM project is created and bound. For more information about how to bind a project in the OSS console, see Quick Start. For more information about how to bind a project using an API, see AttachOSSBucket.
When you parse a blind watermark, the bucket that contains the image must be in the same region as the bucket that was used when the watermark was added. The bucket must also be bound to the same IMM project that was used to add the watermark. Otherwise, the watermark cannot be parsed.
Add a blind watermark
Action: image/blindwatermark
Watermark encoding steps
Encode the content in Base64.
You can replace parts of the encoding in the result.
Replace plus signs (+) with hyphens (-).
Replace forward slashes (/) with underscores (_).
Omit all equal signs (=) from the end of the string.
Java
The SDK for Java 3.17.4 or later is required.
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.GenericResult;
import com.aliyun.oss.model.ProcessObjectRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Formatter;
public class Demo {
public static void main(String[] args) throws IOException {
// In this example, the endpoint is set to China (Hangzhou). Set the endpoint to the actual region.
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// Specify the region ID that corresponds to the endpoint. For example, if the endpoint is oss-cn-hangzhou.aliyuncs.com, set the region to cn-hangzhou.
String region = "cn-hangzhou";
// Do not hard-code access credentials in your project. Otherwise, the access credentials may be leaked, which compromises the security of all your resources.
// In this example, the access credentials are obtained from environment variables. Before you run the sample code, configure the environment variables.
EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();
// Specify the bucket name. Example: examplebucket.
String bucketName = "examplebucket";
// Specify the name of the source image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
String sourceImage = "sourceDir/source.jpg";
// Create an OSSClient instance.
// When the OSSClient instance is no longer used, call the shutdown method to release the resources.
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// Explicitly declare the use of the V4 signature algorithm.
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
StringBuilder sbStyle = new StringBuilder();
Formatter styleFormatter = new Formatter(sbStyle);
// Specify the watermark content.
String content = "Copyright © Alibaba Cloud";
// Add a blind watermark.
// Fix: Use URL-safe Base64 encoding and remove padding.
String encodedContentStr = java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(content.getBytes(StandardCharsets.UTF_8));
String styleType = "image/blindwatermark,content_" + encodedContentStr;
// Specify the name of the processed image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg.
String targetImage = "targetDir/target.jpg";
// Fix: Use URL-safe Base64 encoding and remove padding for targetImage and bucketName.
styleFormatter.format("%s|sys/saveas,o_%s,b_%s",
styleType,
java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(targetImage.getBytes(StandardCharsets.UTF_8)),
java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(bucketName.getBytes(StandardCharsets.UTF_8)));
System.out.println(sbStyle.toString());
ProcessObjectRequest request = new ProcessObjectRequest(bucketName, sourceImage, sbStyle.toString());
GenericResult processResult = ossClient.processObject(request);
BufferedReader reader = new BufferedReader(new InputStreamReader(processResult.getResponse().getContent(), StandardCharsets.UTF_8));
StringBuilder responseContent = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseContent.append(line).append("\n");
}
reader.close();
processResult.getResponse().getContent().close();
System.out.println(responseContent.toString());
} catch (OSSException oe) {
System.err.println("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
System.err.println("Error Message:" + oe.getErrorMessage());
System.err.println("Error Code:" + oe.getErrorCode());
System.err.println("Request ID:" + oe.getRequestId());
System.err.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.err.println("Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.");
System.err.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}Python
The SDK for Python 2.18.4 or later is required.
# -*- coding: utf-8 -*-
import base64
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Specify the endpoint of the region in which the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
# Specify the name of the bucket where the source image is stored.
source_bucket_name = 'source_bucket_name'
# Specify the name of the bucket where you want to store the processed image. This bucket must be in the same region as the source bucket.
target_bucket_name = 'target_bucket_name'
# Specify the name of the source image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
source_image_name = 'sourceDir/source.jpg'
# Create a bucket instance. All file-related methods must be called on the bucket instance.
bucket = oss2.Bucket(auth, endpoint, source_bucket_name)
# Specify the watermark content.
content = 'Copyright © Alibaba Cloud'
# Add a blind watermark.
style = "image/blindwatermark,content_{0}".format(oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(content))))
# Specify the name of the processed image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg.
target_image_name = 'targetDir/target.jpg'
process = "{0}|sys/saveas,o_{1},b_{2}".format(style,
oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(target_image_name))),
oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(target_bucket_name))))
result = bucket.process_object(source_image_name, process)
print(result)Go
The SDK for Go 3.0.2 or later is required.
package main
import (
"encoding/base64"
"fmt"
"os"
"strings"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// Create an OSSClient instance.
// Set yourEndpoint to the endpoint of the bucket. In this example, the endpoint of a bucket in the China (Hangzhou) region is used. Set the endpoint to the actual endpoint.
client, err := oss.New("https://oss-cn-hangzhou.aliyuncs.com", "", "", oss.SetCredentialsProvider(&provider))
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// Specify the name of the bucket where the source image is stored.
bucketName := "srcbucket"
bucket, err := client.Bucket(bucketName)
if err != nil {
handleError(err)
return
}
// Specify the name of the source image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
sourceImageName := "sourceDir/source.jpg"
// Specify the name of the bucket where you want to store the processed image. This bucket must be in the same region as the source bucket.
targetBucketName := "destbucket"
// Specify the name of the processed image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg.
targetImageName := "targetDir/target.jpg"
// Specify the watermark content.
content := "Copyright © Alibaba Cloud"
// Generate a blind watermark. As required by the documentation, you must remove the trailing equal signs (=).
encodedContent := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(content)), "=")
style := fmt.Sprintf("image/blindwatermark,content_%s", encodedContent)
// You must also remove the trailing equal signs (=) from the encoded target image name and bucket name.
encodedImageName := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(targetImageName)), "=")
encodedBucketName := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(targetBucketName)), "=")
process := fmt.Sprintf("%s|sys/saveas,o_%s,b_%s", style, encodedImageName, encodedBucketName)
result, err := bucket.ProcessObject(sourceImageName, process)
if err != nil {
handleError(err)
} else {
fmt.Println(result)
}
}
func handleError(err error) {
fmt.Println("Error:", err)
os.Exit(-1)
}PHP
The SDK for PHP 2.7.0 or later is required.
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\OssClient;
use OSS\Core\OssException;
// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");
// Set yourEndpoint to the endpoint of the bucket. For example, if the bucket is in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
$endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// Specify the bucket name. Example: examplebucket.
$bucket = "examplebucket";
// Specify the name of the source image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
$object = "sourceDir/source.jpg";
// Specify the name of the processed image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: targetDir/target.jpg.
$save_object = "targetDir/target.jpg";
function base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false);
// If the target image is not in the specified bucket, upload the image to the target bucket.
// $ossClient->uploadFile($bucket, $object, "D:\\localpath\\exampleobject.jpg");
$content = "Copyright © Alibaba Cloud";
// Add a blind watermark to the image.
$encodedContent = base64url_encode($content);
$style = "image/blindwatermark,content_$encodedContent";
$process = $style .
'|sys/saveas,' .
'o_' . base64url_encode($save_object) .
',b_' . base64url_encode($bucket);
// Name the processed image example-new.png and save it to the current bucket.
$result = $ossClient->processObject($bucket, $object, $process);
// Print the processing result.
print_r($result);
} catch (OssException $e) {
echo "Error: " . $e->getMessage();
}
?>For more information about using other languages, see Save As.
Parse a blind watermark
Action: image/deblindwatermark
Java
The SDK for Java 3.17.4 or later is required.
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.AsyncProcessObjectRequest;
import com.aliyun.oss.model.AsyncProcessObjectResult;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Demo {
public static void main(String[] args) throws IOException {
// In this example, the endpoint is set to China (Hangzhou). Set the endpoint to the actual region.
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// Specify the region ID that corresponds to the endpoint. For example, if the endpoint is oss-cn-hangzhou.aliyuncs.com, set the region to cn-hangzhou.
String region = "cn-hangzhou";
// Do not hard-code access credentials in your project. Otherwise, the access credentials may be leaked, which compromises the security of all your resources.
// In this example, the access credentials are obtained from environment variables. Before you run the sample code, configure the environment variables.
EnvironmentVariableCredentialsProvider credentialsProvider = new EnvironmentVariableCredentialsProvider();
// Specify the bucket name. Example: examplebucket.
String bucketName = "target_bucket_name";
// Specify the name of the source image. If the image is not in the root directory of the bucket, you must specify the full path of the image. Example: sourceDir/source.jpg.
String sourceKey = "targetDir/target.jpg";
// The topic of the MNS message.
String topic = "imm-blindwatermark-test";
// Create an OSSClient instance.
// When the OSSClient instance is no longer used, call the shutdown method to release the resources.
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// Explicitly declare the use of the V4 signature algorithm.
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
// Extract the watermark content from the specified image.
String style = "image/deblindwatermark,s_low,t_text";
String encodedTopic = Base64.getUrlEncoder().withoutPadding().encodeToString(topic.getBytes(StandardCharsets.UTF_8));
String process = String.format("%s|sys/notify,topic_%s", style, encodedTopic);
AsyncProcessObjectRequest request = new AsyncProcessObjectRequest(bucketName, sourceKey, process);
// Call the asynchronous stream processing interface.
AsyncProcessObjectResult result = ossClient.asyncProcessObject(request);
System.out.println(result.getRequestId());
// Obtain and print the TaskId to track the spooling task.
System.out.println(result.getTaskId());
} catch (OSSException oe) {
System.err.println("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
System.err.println("Error Message:" + oe.getErrorMessage());
System.err.println("Error Code:" + oe.getErrorCode());
System.err.println("Request ID:" + oe.getRequestId());
System.err.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.err.println("Caught an ClientException, which means the client encountered a serious internal problem while trying to communicate with OSS, such as not being able to access the network.");
System.err.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}Python
The SDK for Python 2.18.4 or later is required.
# -*- coding: utf-8 -*-
import base64
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider
# Specify the endpoint of the region in which the bucket is located. For example, if the bucket is in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com.
endpoint = 'https://oss-cn-hangzhou.aliyuncs.com'
# Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
# Specify the bucket name.
bucket_name = 'target_bucket_name'
# Specify the name of the watermarked image file.
source_key = 'targetDir/target.jpg'
# Specify the topic of the MNS message.
topic = 'imm-blindwatermark-test'
# Create a bucket instance. All file-related methods must be called on the bucket instance.
bucket = oss2.Bucket(auth, endpoint, bucket_name)
# Extract the watermark content from the specified image.
style = 'image/deblindwatermark,s_low,t_text'
process = "{0}|sys/notify,topic_{1}".format(style,
oss2.compat.to_string(base64.urlsafe_b64encode(oss2.compat.to_bytes(topic))).replace('=', ''))
# Call the asynchronous stream processing interface.
result = bucket.async_process_object(source_key, process)
# The unique ID of the OSS API request, used for tracking and troubleshooting.
print(result.request_id)
# The unique ID of the task, used to track the spooling task.
print(result.task_id)
Go
The SDK for Go 3.0.2 or later is required.
package main
import (
"encoding/base64"
"fmt"
"os"
"strings"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
provider, err := oss.NewEnvironmentVariableCredentialsProvider()
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// Create an OSSClient instance.
endpoint := "https://oss-cn-hangzhou.aliyuncs.com"
client, err := oss.New(endpoint, "", "", oss.SetCredentialsProvider(provider))
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// Specify the bucket name.
bucketName := "target_bucket_name"
bucket, err := client.Bucket(bucketName)
if err != nil {
handleError(err)
return
}
// Specify the name of the watermarked image file.
sourceKey := "targetDir/target.jpg"
// Specify the topic of the MNS message.
topic := "imm-blindwatermark-test"
// Extract the watermark content from the specified image.
style := "image/deblindwatermark,s_low,t_text"
encodedTopic := strings.TrimRight(base64.URLEncoding.EncodeToString([]byte(topic)), "=")
process := fmt.Sprintf("%s|sys/notify,topic_%s", style, encodedTopic)
// Call the asynchronous stream processing interface.
result, err := bucket.AsyncProcessObject(sourceKey, process)
if err != nil {
handleError(err)
} else {
fmt.Println(result.RequestID) // The unique ID of the OSS API request.
fmt.Println(result.TaskId) // The unique ID of the task, used to track the spooling task.
}
}
func handleError(err error) {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(-1)
}PHP
The SDK for PHP 2.7.0 or later is required.
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
// Obtain access credentials from environment variables. Before you run the sample code, make sure that the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables are configured.
$accessKeyId = getenv("OSS_ACCESS_KEY_ID");
$accessKeySecret = getenv("OSS_ACCESS_KEY_SECRET");
// In this example, the endpoint is set to China (Hangzhou). Set the endpoint to the actual region.
$endpoint = 'https://oss-cn-hangzhou.aliyuncs.com';
// Specify the bucket name.
$bucketName = 'target_bucket_name';
// Specify the name of the watermarked image file.
$sourceKey = 'targetDir/target.jpg';
function base64_url_encode($input) {
return strtr(base64_encode($input), '+/', '-_');
}
try {
// Create an OSSClient instance.
$client = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
// Specify the topic of the MNS message.
$topic = 'imm-blindwatermark-test';
// Extract the watermark content from the specified image.
$style = 'image/deblindwatermark,s_low,t_text';
$encodedTopic = str_replace('=', '', base64_url_encode($topic));
$process = sprintf('%s|sys/notify,topic_%s', $style, $encodedTopic);
// Call the asynchronous stream processing interface.
$result = $client->asyncProcessObject($bucketName, $sourceKey, $process);
// Parse the returned JSON string.
$decodedResult = json_decode($result, true);
echo "Request ID: " . $decodedResult['RequestId'] . "\n"; // The unique ID of the OSS API request.
echo "TaskId: " . $decodedResult['TaskId'] . "\n"; // The unique ID of the task, used to track the spooling task.
} catch (OssException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>
For more information about how to use other SDKs, see Asynchronous processing.
Parameters
Add a blind watermark
Action: image/blindwatermark
Parameter | Required | Description | Valid values |
content | No | The watermark text must be URL-safe Base64-encoded. You can use the watermark encoding tool to encode the text. For example, if the text is | The watermark string before Base64 encoding can be up to 256 characters in length. |
s | No | The strength of the watermark. A higher strength provides better resistance to attacks but causes more noticeable image distortion. |
|
q | No | The quality of the output watermarked image. A higher quality results in a larger image size and better watermark parsing quality. Note This parameter takes effect only if the input image is in the JPG format. | Default value: 90. Valid values: 70 to 100. |
The sys/saveas parameter is also used when you add a blind watermark to an image. For more information, see Save an image to a specified location.
Parse a blind watermark
Action: image/deblindwatermark
Parameter | Required | Description | Valid values |
s | No | The watermark extraction level. A higher level takes longer but provides better results. |
|
t | No | The type of the embedded watermark. | Text: text |
The notify parameter is also used when you parse a blind watermark. For more information, see Notifications.
Related API operations
If your application requires a high level of customization, you can send REST API requests directly. To send REST API requests, you must manually write code to calculate the signature. For more information about how to calculate the Authorization request header, see Signature V4 (recommended). To add or parse a blind watermark, you must send a POST request and include the parameters in the request body.
Add a blind watermark
You can process images by adding the x-oss-process parameter to a PostObject request. For more information, see PostObject.
Processing example
POST /example.jpg?x-oss-process HTTP/1.1
Host: image-demo.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 28 Oct 2022 06:40:10 GMT
Authorization: SignatureValue
// The watermark content is "Alibaba Cloud Copyright", the watermark strength is low, the output image quality is 90, and the watermarked image is saved to oss://image-demo/outobjprefix.jpg.
x-oss-process=image/blindwatermark,content_6Zi_6YeM5LqR54mI5p2D5omA5pyJ,s_low,q_90|sys/saveas,b_aW1hZ2UtZGVtbw,o_b3V0b2JqcHJlZml4LmpwZwParse a blind watermark
You can process images by adding image scaling parameters to the PostObject API. For more information, see PostObject.
Processing example
POST /outobjprefix.jpg?x-oss-async-process HTTP/1.1
Host: image-demo.oss-cn-hangzhou.aliyuncs.com
Date: Fri, 28 Oct 2022 06:40:10 GMT
Authorization: SignatureValue
// Parse the watermark that was added in the previous step. The message topic is doc-images.
x-oss-async-process=image/deblindwatermark|sys/notify,topic_ZG9jLWltYWdlcwNotes
The blind watermarking feature supports only images in the JPG, PNG, BMP, WebP, and TIFF formats.
The minimum width and height of an image are 80 pixels. The maximum width and height are 10,000 pixels.
The ratio of the shorter edge to the longer edge must be greater than 1:2.
The blind watermarking feature does not support solid black, solid white, or low-resolution images. For example, images with a resolution of less than 200 × 200 pixels may not be supported, although this is not a strict limit.
Only text watermarks are supported.
Anonymous access is not supported.