[Libreoffice-commits] online.git: common/Session.cpp common/Session.hpp kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp test/WhiteBoxTests.cpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/reference.txt wsd/Storage.cpp wsd/Storage.hpp
Marco Cecchetti
marco.cecchetti at collabora.com
Thu Sep 7 15:29:02 UTC 2017
common/Session.cpp | 6 ++
common/Session.hpp | 3 +
kit/ChildSession.cpp | 6 +-
kit/ChildSession.hpp | 4 +
kit/Kit.cpp | 143 +++++++++++++++++++++++++++++++++++++++++++++++--
test/WhiteBoxTests.cpp | 3 -
wsd/ClientSession.cpp | 7 ++
wsd/ClientSession.hpp | 1
wsd/DocumentBroker.cpp | 3 +
wsd/Storage.cpp | 4 +
wsd/Storage.hpp | 4 +
wsd/reference.txt | 4 +
12 files changed, 180 insertions(+), 8 deletions(-)
New commits:
commit dee39a562c6fd8f1eed3567e7f9e518f1d599bd7
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date: Mon Sep 4 15:40:04 2017 +0200
support for rendering a watermark on each tile
Change-Id: I3edccac49a3bcd3d2493d8d7ef3a1ae29307e727
Reviewed-on: https://gerrit.libreoffice.org/41898
Reviewed-by: Jan Holesovsky <kendy at collabora.com>
Tested-by: Jan Holesovsky <kendy at collabora.com>
diff --git a/common/Session.cpp b/common/Session.cpp
index 97933c83..c58eb3f0 100644
--- a/common/Session.cpp
+++ b/common/Session.cpp
@@ -136,6 +136,12 @@ void Session::parseDocOptions(const std::vector<std::string>& tokens, int& part,
_lang = tokens[i].substr(strlen("lang="));
++offset;
}
+ else if (tokens[i].find("watermarkText=") == 0)
+ {
+ const std::string watermarkText = tokens[i].substr(strlen("watermarkText="));
+ Poco::URI::decode(watermarkText, _watermarkText);
+ ++offset;
+ }
}
if (tokens.size() > offset)
diff --git a/common/Session.hpp b/common/Session.hpp
index 5a154ee1..99961848 100644
--- a/common/Session.hpp
+++ b/common/Session.hpp
@@ -157,6 +157,9 @@ protected:
/// Extra info per user, mostly mail, avatar, links, etc.
std::string _userExtraInfo;
+ /// In case a watermark has to be rendered on each tile.
+ std::string _watermarkText;
+
/// Language for the document based on what the user has in the UI.
std::string _lang;
};
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 553f6dbf..56075790 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -329,7 +329,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, const s
std::unique_lock<std::recursive_mutex> lock(Mutex);
- bool loaded = _docManager.onLoad(getId(), _jailedFilePath, _userName, _docPassword, renderOpts, _haveDocPassword, _lang);
+ bool loaded = _docManager.onLoad(getId(), _jailedFilePath, _userName,
+ _docPassword, renderOpts, _haveDocPassword, _lang, _watermarkText);
if (!loaded || _viewId < 0)
{
LOG_ERR("Failed to get LoKitDocument instance.");
@@ -400,7 +401,8 @@ bool ChildSession::sendFontRendering(const char* /*buffer*/, int /*length*/, con
std::memcpy(output.data(), response.data(), response.size());
Timestamp timestamp;
- int width, height;
+ // renderFont use a default font size (25) when width and height are 0
+ int width = 0, height = 0;
unsigned char* ptrFont = nullptr;
{
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 446ffca4..c3af96ec 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -37,7 +37,8 @@ public:
const std::string& docPassword,
const std::string& renderOpts,
const bool haveDocPassword,
- const std::string& lang) = 0;
+ const std::string& lang,
+ const std::string& watermarkText) = 0;
/// Unload a client session, which unloads the document
/// if it is the last and only.
@@ -143,6 +144,7 @@ public:
const std::string& getViewUserId() const { return _userId; }
const std::string& getViewUserName() const { return _userName; }
const std::string& getViewUserExtraInfo() const { return _userExtraInfo; }
+ const std::string& getWatermarkText() const { return _watermarkText; }
void updateSpeed();
int getSpeed();
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index a5a2be25..63ebdbe1 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -479,6 +479,119 @@ public:
}
};
+class Watermark
+{
+public:
+ Watermark(std::shared_ptr<lok::Document> loKitDoc, std::string text)
+ : _loKitDoc(loKitDoc)
+ , _text(text)
+ , _font("Liberation Sans")
+ , _width(0)
+ , _height(0)
+ , _color{64, 64, 64}
+ , _alphaLevel(0.2)
+ , _pixmap(nullptr)
+ {
+ }
+
+ ~Watermark()
+ {
+ if (_pixmap)
+ std::free(_pixmap);
+ }
+
+ void blending(unsigned char* tilePixmap,
+ int offsetX, int offsetY,
+ int tilesPixmapWidth, int tilesPixmapHeight,
+ int tileWidth, int tileHeight,
+ LibreOfficeKitTileMode mode)
+ {
+ // set requested watermark size a little bit smaller than tile size
+ int width = tileWidth * 0.9;
+ int height = tileHeight * 0.9;
+
+ const unsigned char* pixmap = getPixmap(width, height);
+
+ if (pixmap && tilePixmap)
+ {
+ unsigned int pixmapSize = tilesPixmapWidth * tilesPixmapHeight * 4;
+ int maxX = std::min(tileWidth, _width);
+ int maxY = std::min(tileHeight, _height);
+
+ // center watermark
+ offsetX += (tileWidth - maxX) / 2;
+ offsetY += (tileHeight - maxY) / 2;
+
+ for (int y = 0; y < maxY; ++y)
+ {
+ for (int x = 0; x < maxX; ++x)
+ {
+ unsigned int i = (y * _width + x) * 4;
+ unsigned int alpha = pixmap[i + 3];
+ if (alpha)
+ {
+ for (int h = 0; h < 3; ++h)
+ {
+ unsigned int j = ((y + offsetY) * tilesPixmapWidth + (x + offsetX)) * 4 + h;
+ if (j < pixmapSize)
+ {
+ unsigned int color = (mode == LOK_TILEMODE_BGRA) ? _color[2 - h] : _color[h];
+
+ // original alpha blending for smoothing text edges
+ color = ((color * alpha) + tilePixmap[j] * (255 - alpha)) / 255;
+ // blending between document tile and watermark
+ tilePixmap[j] = color * _alphaLevel + tilePixmap[j] * (1 - _alphaLevel);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+private:
+ const unsigned char* getPixmap(int width, int height)
+ {
+ if (_pixmap && width == _width && height == _height)
+ return _pixmap;
+
+ if (_pixmap)
+ std::free(_pixmap);
+
+ _width = width;
+ _height = height;
+
+ if (!_loKitDoc)
+ {
+ LOG_ERR("Watermark rendering requested without a valid document.");
+ return nullptr;
+ }
+
+ // renderFont returns a buffer based on RGBA mode, where r, g, b
+ // are always set to 0 (black) and the alpha level is 0 everywhere
+ // except on the text area; the alpha level take into account of
+ // performing anti-aliasing over the text edges.
+ _pixmap = _loKitDoc->renderFont(_font.c_str(), _text.c_str(), &_width, &_height);
+
+ if (!_pixmap)
+ {
+ LOG_ERR("Watermark: rendering failed.");
+ }
+
+ return _pixmap;
+ }
+
+private:
+ std::shared_ptr<lok::Document> _loKitDoc;
+ std::string _text;
+ std::string _font;
+ int _width;
+ int _height;
+ unsigned char _color[3];
+ double _alphaLevel;
+ unsigned char* _pixmap;
+};
+
static FILE* ProcSMapsFile = nullptr;
/// A document container.
@@ -515,6 +628,7 @@ public:
_haveDocPassword(false),
_isDocPasswordProtected(false),
_docPasswordType(PasswordType::ToView),
+ _docWatermark(),
_stop(false),
_isLoading(0),
_editorId(-1),
@@ -708,6 +822,12 @@ public:
// Send back the request with all optional parameters given in the request.
std::string response = ADD_DEBUG_RENDERID(tile.serialize("tile:")) + "\n";
+ int pixelWidth = tile.getWidth();
+ int pixelHeight = tile.getHeight();
+
+ if (_docWatermark)
+ _docWatermark->blending(pixmap.data(), 0, 0, pixelWidth, pixelHeight, pixelWidth, pixelHeight, mode);
+
std::vector<char> output;
output.reserve(response.size() + pixmapDataSize);
output.resize(response.size());
@@ -812,6 +932,15 @@ public:
continue;
}
+ int offsetX = positionX * pixelWidth;
+ int offsetY = positionY * pixelHeight;
+
+ if (_docWatermark)
+ _docWatermark->blending(pixmap.data(), offsetX, offsetY,
+ pixmapWidth, pixmapHeight,
+ tileCombined.getWidth(), tileCombined.getHeight(),
+ mode);
+
if (!_pngCache.encodeSubBufferToPNG(pixmap.data(), positionX * pixelWidth, positionY * pixelHeight,
pixelWidth, pixelHeight, pixmapWidth, pixmapHeight, output, mode,
hash, wireId, oldWireId))
@@ -984,7 +1113,8 @@ private:
const std::string& docPassword,
const std::string& renderOpts,
const bool haveDocPassword,
- const std::string& lang) override
+ const std::string& lang,
+ const std::string& watermarkText) override
{
std::unique_lock<std::mutex> lock(_mutex);
@@ -1013,7 +1143,7 @@ private:
try
{
- if (!load(session, uri, userName, docPassword, renderOpts, haveDocPassword, lang))
+ if (!load(session, uri, userName, docPassword, renderOpts, haveDocPassword, lang, watermarkText))
{
return false;
}
@@ -1255,7 +1385,8 @@ private:
const std::string& docPassword,
const std::string& renderOpts,
const bool haveDocPassword,
- const std::string& lang)
+ const std::string& lang,
+ const std::string& watermarkText)
{
const std::string sessionId = session->getId();
@@ -1320,6 +1451,9 @@ private:
// Only save the options on opening the document.
// No support for changing them after opening a document.
_renderOpts = renderOpts;
+
+ if (!watermarkText.empty())
+ _docWatermark.reset(new Watermark(_loKitDocument, watermarkText));
}
else
{
@@ -1665,6 +1799,9 @@ private:
// Whether password is required to view the document, or modify it
PasswordType _docPasswordType;
+ // Document watermark
+ std::unique_ptr<Watermark> _docWatermark;
+
std::atomic<bool> _stop;
mutable std::mutex _mutex;
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 7c9b13f2..665d0bfe 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -336,7 +336,8 @@ public:
const std::string& /*docPassword*/,
const std::string& /*renderOpts*/,
const bool /*haveDocPassword*/,
- const std::string& /*lang*/) override
+ const std::string& /*lang*/,
+ const std::string& /*watermarkText*/) override
{
return false;
}
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 7a4f42b0..2e455029 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -316,6 +316,13 @@ bool ClientSession::loadDocument(const char* /*buffer*/, int /*length*/,
oss << " lang=" << _lang;
}
+ if (!_watermarkText.empty())
+ {
+ std::string encodedWatermarkText;
+ Poco::URI::encode(_watermarkText, "", encodedWatermarkText);
+ oss << " watermarkText=" << encodedWatermarkText;
+ }
+
if (!_docOptions.empty())
{
oss << " options=" << _docOptions;
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index fd3a05b8..dd2ea0e3 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -47,6 +47,7 @@ public:
void setUserId(const std::string& userId) { _userId = userId; }
void setUserName(const std::string& userName) { _userName = userName; }
void setUserExtraInfo(const std::string& userExtraInfo) { _userExtraInfo = userExtraInfo; }
+ void setWatermarkText(const std::string& watermarkText) { _watermarkText = watermarkText; }
void setDocumentOwner(const bool documentOwner) { _isDocumentOwner = documentOwner; }
bool isDocumentOwner() const { return _isDocumentOwner; }
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 11f40004..9154abab 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -424,6 +424,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
// Call the storage specific fileinfo functions
std::string userid, username;
std::string userExtraInfo;
+ std::string watermarkText;
std::chrono::duration<double> getInfoCallDuration(0);
WopiStorage* wopiStorage = dynamic_cast<WopiStorage*>(_storage.get());
if (wopiStorage != nullptr)
@@ -432,6 +433,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
userid = wopifileinfo->_userid;
username = wopifileinfo->_username;
userExtraInfo = wopifileinfo->_userExtraInfo;
+ watermarkText = wopifileinfo->_watermarkText;
if (!wopifileinfo->_userCanWrite ||
LOOLWSD::IsViewFileExtension(wopiStorage->getFileExtension()))
@@ -509,6 +511,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
session->setUserId(userid);
session->setUserName(username);
session->setUserExtraInfo(userExtraInfo);
+ session->setWatermarkText(watermarkText);
// Basic file information was stored by the above getWOPIFileInfo() or getLocalFileInfo() calls
const auto fileInfo = _storage->getFileInfo();
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 7cf22e56..c6b4339f 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -540,6 +540,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au
std::string userId;
std::string userName;
std::string userExtraInfo;
+ std::string watermarkText;
bool canWrite = false;
bool enableOwnerTermination = false;
std::string postMessageOrigin;
@@ -562,6 +563,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au
getWOPIValue(object, "UserId", userId);
getWOPIValue(object, "UserFriendlyName", userName);
getWOPIValue(object, "UserExtraInfo", userExtraInfo);
+ getWOPIValue(object, "WatermarkText", watermarkText);
getWOPIValue(object, "UserCanWrite", canWrite);
getWOPIValue(object, "PostMessageOrigin", postMessageOrigin);
getWOPIValue(object, "HidePrintOption", hidePrintOption);
@@ -583,7 +585,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au
const Poco::Timestamp modifiedTime = iso8601ToTimestamp(lastModifiedTime);
_fileInfo = FileInfo({filename, ownerId, modifiedTime, size});
- return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo({userId, userName, userExtraInfo, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, disableInactiveMessages, callDuration}));
+ return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo({userId, userName, userExtraInfo, watermarkText, canWrite, postMessageOrigin, hidePrintOption, hideSaveOption, hideExportOption, enableOwnerTermination, disablePrint, disableExport, disableCopy, disableInactiveMessages, callDuration}));
}
/// PutRelativeFile - uri format: http://server/<...>/wopi*/files/<id>/
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 28532e4a..2c619581 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -198,6 +198,7 @@ public:
WOPIFileInfo(const std::string& userid,
const std::string& username,
const std::string& userExtraInfo,
+ const std::string& watermarkText,
const bool userCanWrite,
const std::string& postMessageOrigin,
const bool hidePrintOption,
@@ -211,6 +212,7 @@ public:
const std::chrono::duration<double> callDuration)
: _userid(userid),
_username(username),
+ _watermarkText(watermarkText),
_userCanWrite(userCanWrite),
_postMessageOrigin(postMessageOrigin),
_hidePrintOption(hidePrintOption),
@@ -232,6 +234,8 @@ public:
std::string _username;
/// Extra info per user, typically mail and other links, as json.
std::string _userExtraInfo;
+ /// In case a watermark has to be rendered on each tile.
+ std::string _watermarkText;
/// If user accessing the file has write permission
bool _userCanWrite;
/// WOPI Post message property
diff --git a/wsd/reference.txt b/wsd/reference.txt
index 92d0e3e2..4d06850f 100644
--- a/wsd/reference.txt
+++ b/wsd/reference.txt
@@ -73,5 +73,9 @@ EnableOwnerTermination
If set to true, it allows the document owner (the one with OwnerId =
UserId) to send a 'closedocument' message (see protocol.txt)
+WatermarkText
+ If set to a non-empty string, is used for rendering a watermark-like
+ text on each tile of the document
+
Note that it is possible to just hide print,save,export options while still
being able to access them from WOPI hosts using PostMessage API (see loleaflet/reference.html)
More information about the Libreoffice-commits
mailing list