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 15471e1891745fc9ff007b3ca304ab79af7d2764
Author: Marco Cecchetti <marco.cecche...@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/42022
    Reviewed-by: Jan Holesovsky <ke...@collabora.com>
    Tested-by: Jan Holesovsky <ke...@collabora.com>
    (cherry picked from commit 03c82c3dd1268835064cc1a97f9a4c8d7ed4cbba)
    Reviewed-on: https://gerrit.libreoffice.org/42063

diff --git a/common/Session.cpp b/common/Session.cpp
index a1cded5f..c17089e9 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 63872c08..a39d651b 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 7ad0cdd0..bc2ddd31 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 7faef0ed..9a01901e 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 0358e7f9..bdb3ae79 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 825ebb4e..a0c83368 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 7c22b04d..033a963e 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 0002d615..573e21d4 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 568187d9..9d6bff46 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 6eef7e7f..afe69a87 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 405a8c30..a3fbe637 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 ecc09a36..f6ad3985 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)
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to