summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Cecchetti <marco.cecchetti@collabora.com>2017-09-04 15:40:04 +0200
committerJan Holesovsky <kendy@collabora.com>2017-09-07 17:27:01 +0200
commit15471e1891745fc9ff007b3ca304ab79af7d2764 (patch)
tree74487fa95a7be4a8de6544b023a4c50700a6517a
parentBump version for packages 2.1.2-12 (diff)
downloadonline-15471e1891745fc9ff007b3ca304ab79af7d2764.tar.gz
online-15471e1891745fc9ff007b3ca304ab79af7d2764.zip
support for rendering a watermark on each tile
Change-Id: I3edccac49a3bcd3d2493d8d7ef3a1ae29307e727 Reviewed-on: https://gerrit.libreoffice.org/42022 Reviewed-by: Jan Holesovsky <kendy@collabora.com> Tested-by: Jan Holesovsky <kendy@collabora.com> (cherry picked from commit 03c82c3dd1268835064cc1a97f9a4c8d7ed4cbba) Reviewed-on: https://gerrit.libreoffice.org/42063
-rw-r--r--common/Session.cpp6
-rw-r--r--common/Session.hpp3
-rw-r--r--kit/ChildSession.cpp6
-rw-r--r--kit/ChildSession.hpp4
-rw-r--r--kit/Kit.cpp143
-rw-r--r--test/WhiteBoxTests.cpp3
-rw-r--r--wsd/ClientSession.cpp7
-rw-r--r--wsd/ClientSession.hpp1
-rw-r--r--wsd/DocumentBroker.cpp3
-rw-r--r--wsd/Storage.cpp4
-rw-r--r--wsd/Storage.hpp4
-rw-r--r--wsd/reference.txt4
12 files changed, 180 insertions, 8 deletions
diff --git a/common/Session.cpp b/common/Session.cpp
index a1cded5f01..c17089e958 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 63872c0814..a39d651b60 100644
--- a/common/Session.hpp
+++ b/common/Session.hpp
@@ -155,6 +155,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 7ad0cdd0ee..bc2ddd31e8 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.");
@@ -398,7 +399,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 7faef0ed03..9a01901e82 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -36,7 +36,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.
@@ -139,6 +140,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 loKitCallback(const int type, const std::string& payload);
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 0358e7f9af..bdb3ae7974 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -426,6 +426,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.
@@ -462,6 +575,7 @@ public:
_haveDocPassword(false),
_isDocPasswordProtected(false),
_docPasswordType(PasswordType::ToView),
+ _docWatermark(),
_stop(false),
_isLoading(0)
{
@@ -653,6 +767,12 @@ public:
return;
}
+ int pixelWidth = tile.getWidth();
+ int pixelHeight = tile.getHeight();
+
+ if (_docWatermark)
+ _docWatermark->blending(pixmap.data(), 0, 0, pixelWidth, pixelHeight, pixelWidth, pixelHeight, mode);
+
if (!_pngCache.encodeBufferToPNG(pixmap.data(), tile.getWidth(), tile.getHeight(), output, mode, hash))
{
//FIXME: Return error.
@@ -749,6 +869,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))
{
@@ -924,7 +1053,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);
@@ -953,7 +1083,7 @@ private:
try
{
- if (!load(session, uri, userName, docPassword, renderOpts, haveDocPassword, lang))
+ if (!load(session, uri, userName, docPassword, renderOpts, haveDocPassword, lang, watermarkText))
{
return false;
}
@@ -1145,7 +1275,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();
@@ -1210,6 +1341,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
{
@@ -1553,6 +1687,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 825ebb4e6e..a0c833685a 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -321,7 +321,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 7c22b04d87..033a963e72 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -290,6 +290,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 0002d6152d..573e21d4cc 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 568187d919..9d6bff46e1 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -395,6 +395,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)
@@ -403,6 +404,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()))
@@ -466,6 +468,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 6eef7e7f89..afe69a87f6 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -484,6 +484,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
std::string userId;
std::string userName;
std::string userExtraInfo;
+ std::string watermarkText;
bool canWrite = false;
bool enableOwnerTermination = false;
std::string postMessageOrigin;
@@ -510,6 +511,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
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);
@@ -552,7 +554,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const st
_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}));
}
/// uri format: http://server/<...>/wopi*/files/<id>/content
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 405a8c3056..a3fbe637fb 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -185,6 +185,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,
@@ -198,6 +199,7 @@ public:
const std::chrono::duration<double> callDuration)
: _userid(userid),
_username(username),
+ _watermarkText(watermarkText),
_userCanWrite(userCanWrite),
_postMessageOrigin(postMessageOrigin),
_hidePrintOption(hidePrintOption),
@@ -219,6 +221,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 ecc09a3671..f6ad3985c9 100644
--- a/wsd/reference.txt
+++ b/wsd/reference.txt
@@ -55,5 +55,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)