mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2024-11-28 19:14:14 +01:00
Merge branch 'feature/KASM-5466_watermark_rotation' into 'master'
Implement rotated watermark text support Closes KASM-5466 See merge request kasm-technologies/internal/KasmVNC!121
This commit is contained in:
commit
4f726e3654
@ -201,6 +201,10 @@ rfb::IntParameter rfb::Server::DLP_WatermarkTimeOffsetMinutes
|
||||
("DLP_WatermarkTimeOffsetMinutes",
|
||||
"Offset from UTC for -DLP_WatermarkText, minutes",
|
||||
0, -24 * 60, 24 * 60);
|
||||
rfb::IntParameter rfb::Server::DLP_WatermarkTextAngle
|
||||
("DLP_WatermarkTextAngle",
|
||||
"Angle for -DLP_WatermarkText rotation",
|
||||
0, -359, 359);
|
||||
rfb::StringParameter rfb::Server::DLP_WatermarkImage
|
||||
("DLP_WatermarkImage",
|
||||
"PNG file to use as a watermark",
|
||||
|
@ -52,6 +52,7 @@ namespace rfb {
|
||||
static IntParameter DLP_WatermarkFontSize;
|
||||
static IntParameter DLP_WatermarkTimeOffset;
|
||||
static IntParameter DLP_WatermarkTimeOffsetMinutes;
|
||||
static IntParameter DLP_WatermarkTextAngle;
|
||||
static StringParameter DLP_ClipLog;
|
||||
static StringParameter DLP_Region;
|
||||
static StringParameter DLP_Clip_Types;
|
||||
|
@ -16,6 +16,7 @@
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -28,6 +29,7 @@
|
||||
#include "font.h"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
#include "Watermark.h"
|
||||
|
||||
@ -187,6 +189,149 @@ static uint32_t drawnwidth(const char *txt) {
|
||||
return x;
|
||||
}
|
||||
|
||||
static void angle2mat(FT_Matrix &mat) {
|
||||
const float angle = Server::DLP_WatermarkTextAngle / 360.f * 2 * -3.14159f;
|
||||
|
||||
mat.xx = (FT_Fixed)( cosf(angle) * 0x10000L);
|
||||
mat.xy = (FT_Fixed)(-sinf(angle) * 0x10000L);
|
||||
mat.yx = (FT_Fixed)( sinf(angle) * 0x10000L);
|
||||
mat.yy = (FT_Fixed)( cosf(angle) * 0x10000L);
|
||||
}
|
||||
|
||||
// Note: w and h are absolute
|
||||
static void angledstr(uint8_t *buf, const char *txt, const uint32_t x_, const uint32_t y_,
|
||||
const uint32_t w, const uint32_t h,
|
||||
const uint32_t stride, const bool invx, const bool invy) {
|
||||
|
||||
unsigned ucs[256], i, ucslen;
|
||||
unsigned len = strlen(txt);
|
||||
i = 0;
|
||||
ucslen = 0;
|
||||
while (len > 0 && txt[i]) {
|
||||
size_t ret = rfb::utf8ToUCS4(&txt[i], len, &ucs[ucslen]);
|
||||
i += ret;
|
||||
len -= ret;
|
||||
ucslen++;
|
||||
}
|
||||
|
||||
FT_Matrix mat;
|
||||
FT_Vector pen;
|
||||
|
||||
angle2mat(mat);
|
||||
|
||||
pen.x = 0;
|
||||
pen.y = 0;
|
||||
|
||||
uint32_t x, y;
|
||||
|
||||
x = x_;
|
||||
y = y_;
|
||||
for (i = 0; i < ucslen; i++) {
|
||||
FT_Set_Transform(face, &mat, &pen);
|
||||
|
||||
if (FT_Load_Char(face, ucs[i], FT_LOAD_RENDER))
|
||||
continue;
|
||||
const FT_Bitmap * const map = &(face->glyph->bitmap);
|
||||
|
||||
uint32_t row, col;
|
||||
for (row = 0; row < (uint32_t) map->rows; row++) {
|
||||
int ny = row + y - face->glyph->bitmap_top;
|
||||
if (ny < 0)
|
||||
continue;
|
||||
if ((unsigned) ny >= h)
|
||||
continue;
|
||||
|
||||
uint8_t *dst = (uint8_t *) buf;
|
||||
dst += ny * stride + x;
|
||||
|
||||
const uint8_t *src = map->buffer + map->pitch * row;
|
||||
for (col = 0; col < (uint32_t) map->width; col++) {
|
||||
if (col + x >= w)
|
||||
continue;
|
||||
const uint8_t out = (src[col] + 8) >> 4;
|
||||
dst[col] |= out < 16 ? out : 15;
|
||||
}
|
||||
}
|
||||
|
||||
x += face->glyph->advance.x >> 6;
|
||||
|
||||
pen.x += face->glyph->advance.x;
|
||||
pen.y += face->glyph->advance.y;
|
||||
}
|
||||
}
|
||||
|
||||
static void angledsize(const char *txt, uint32_t &w, uint32_t &h,
|
||||
uint32_t &recw, uint32_t &recy,
|
||||
bool &invx, bool &invy) {
|
||||
|
||||
unsigned ucs[256], i, ucslen;
|
||||
unsigned len = strlen(txt);
|
||||
i = 0;
|
||||
ucslen = 0;
|
||||
while (len > 0 && txt[i]) {
|
||||
size_t ret = rfb::utf8ToUCS4(&txt[i], len, &ucs[ucslen]);
|
||||
i += ret;
|
||||
len -= ret;
|
||||
ucslen++;
|
||||
}
|
||||
|
||||
FT_Matrix mat;
|
||||
FT_Vector pen;
|
||||
|
||||
angle2mat(mat);
|
||||
|
||||
pen.x = 0;
|
||||
pen.y = 0;
|
||||
|
||||
FT_BBox firstbox, lastbox;
|
||||
|
||||
for (i = 0; i < ucslen; i++) {
|
||||
FT_Set_Transform(face, &mat, &pen);
|
||||
|
||||
if (FT_Load_Char(face, ucs[i], FT_LOAD_DEFAULT))
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
FT_Glyph glyph;
|
||||
|
||||
FT_Get_Glyph(face->glyph, &glyph);
|
||||
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &firstbox);
|
||||
FT_Done_Glyph(glyph);
|
||||
|
||||
// recommended y; if the angle is steep enough, use the X bearing
|
||||
#define EDGE 22
|
||||
const int angle = abs(Server::DLP_WatermarkTextAngle);
|
||||
if ((angle > (45 + EDGE) && angle < (135 - EDGE)) ||
|
||||
(angle > (225 + EDGE) && angle < (315 - EDGE)))
|
||||
recy = face->glyph->metrics.horiBearingX >> 6;
|
||||
else
|
||||
recy = face->glyph->metrics.horiBearingY >> 6;
|
||||
#undef EDGE
|
||||
} else if (i == ucslen - 1) {
|
||||
FT_Glyph glyph;
|
||||
|
||||
FT_Get_Glyph(face->glyph, &glyph);
|
||||
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &lastbox);
|
||||
FT_Done_Glyph(glyph);
|
||||
}
|
||||
|
||||
if (i != ucslen - 1) {
|
||||
pen.x += face->glyph->advance.x;
|
||||
pen.y += face->glyph->advance.y;
|
||||
}
|
||||
}
|
||||
|
||||
// recommended width, used when X is inverted
|
||||
recw = face->size->metrics.max_advance >> 6;
|
||||
|
||||
// The used area is an union of first box, last box, and their relative distance
|
||||
invx = pen.x < 0;
|
||||
invy = pen.y > 0;
|
||||
|
||||
w = (firstbox.xMax - firstbox.xMin) + (lastbox.xMax - lastbox.xMin) + abs(pen.x >> 6);
|
||||
h = (firstbox.yMax - firstbox.yMin) + (lastbox.yMax - lastbox.yMin) + abs(pen.y >> 6);
|
||||
}
|
||||
|
||||
static bool drawtext(const char fmt[], const int16_t utcOff, const char fontpath[],
|
||||
const uint8_t fontsize) {
|
||||
char buf[PATH_MAX];
|
||||
@ -213,6 +358,24 @@ static bool drawtext(const char fmt[], const int16_t utcOff, const char fontpath
|
||||
return false;
|
||||
|
||||
free(watermarkInfo.src);
|
||||
if (Server::DLP_WatermarkTextAngle) {
|
||||
uint32_t w, h, recw, recy = fontsize;
|
||||
bool invx, invy;
|
||||
angledsize(buf, w, h, recw, recy, invx, invy);
|
||||
|
||||
// The max is because a rotated text with the time can change size.
|
||||
// With the max op, at least it will only grow instead of bouncing.
|
||||
w = __rfbmax(w, watermarkInfo.w);
|
||||
h = __rfbmax(h, watermarkInfo.h);
|
||||
|
||||
watermarkInfo.w = w;
|
||||
watermarkInfo.h = h;
|
||||
watermarkInfo.src = (uint8_t *) calloc(w, h);
|
||||
|
||||
angledstr(watermarkInfo.src, buf,
|
||||
invx ? w - recw: 0, invy ? h - recy : recy,
|
||||
w, h, w, invx, invy);
|
||||
} else {
|
||||
const uint32_t h = fontsize + 4;
|
||||
const uint32_t w = drawnwidth(buf);
|
||||
|
||||
@ -221,6 +384,7 @@ static bool drawtext(const char fmt[], const int16_t utcOff, const char fontpath
|
||||
watermarkInfo.src = (uint8_t *) calloc(w, h);
|
||||
|
||||
str(watermarkInfo.src, buf, 0, fontsize, w, h, w);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
6
spec/fixtures/defaults_config.yaml
vendored
6
spec/fixtures/defaults_config.yaml
vendored
@ -50,6 +50,12 @@ data_loss_prevention:
|
||||
# location: 10,10
|
||||
# tint: 255,20,20,128
|
||||
# repeat_spacing: 10
|
||||
#text:
|
||||
# template: "${USER} %H:%M"
|
||||
# font: auto
|
||||
# font_size: 48
|
||||
# timezone_name: Australia/Adelaide
|
||||
# angle: 0
|
||||
logging:
|
||||
level: off
|
||||
|
||||
|
@ -100,6 +100,7 @@ data_loss_prevention:
|
||||
# font: auto
|
||||
# font_size: 48
|
||||
# timezone_name: Australia/Adelaide
|
||||
# angle: 0
|
||||
logging:
|
||||
# "verbose" SETTING LOGS YOUR PRIVATE INFORMATION. Keypresses and clipboard content
|
||||
level: off
|
||||
|
@ -1862,6 +1862,24 @@ sub DefineConfigToCLIConversion {
|
||||
$offset_in_seconds/60;
|
||||
}
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_WatermarkTextAngle',
|
||||
configKeys => [
|
||||
KasmVNC::ConfigKey->new({
|
||||
name => "data_loss_prevention.watermark.text.angle",
|
||||
validator => KasmVNC::CallbackValidator->new({
|
||||
isValidCallback => sub {
|
||||
my $value = shift;
|
||||
|
||||
return 0 unless $value =~ /^\d+$/;
|
||||
|
||||
$value >= -359 && $value <= 359;
|
||||
},
|
||||
errorMessage => "must be in range -359..359"
|
||||
}),
|
||||
})
|
||||
]
|
||||
}),
|
||||
KasmVNC::CliOption->new({
|
||||
name => 'DLP_Log',
|
||||
configKeys => [
|
||||
|
@ -384,6 +384,10 @@ Instead of an image, render this text as the watermark. Takes time formatting op
|
||||
for \fBstrftime\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkTextAngle \fIangle\fP
|
||||
Rotate the text by this many degrees, increasing clockwise. Default \fB0\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_WatermarkFont \fI/path/to/font.ttf\fP
|
||||
Use a different font for -DLP_WatermarkText than the bundled one. TTF and OTF fonts
|
||||
are accepted.
|
||||
|
Loading…
Reference in New Issue
Block a user