Handle target in get-thumbnail

Using target parameter we move cursor to the desired position.
Thumbnail then is created and sent to the client.
Example target: "image7.png|graphic"

Original author was Mert Tümer.

Signed-off-by: Szymon Kłos <szymon.klos@collabora.com>
Change-Id: I170f6af6fd29c420565feca69b8bef034fd91a66
This commit is contained in:
Szymon Kłos 2023-02-24 09:14:03 +01:00 committed by Michael Meeks
parent 3bec642b02
commit e82aa05293
4 changed files with 117 additions and 43 deletions

View file

@ -283,7 +283,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
}
else if (tokens.equals(0, "getthumbnail"))
{
if (tokens.size() < 2)
if (tokens.size() < 3)
{
sendTextFrameAndLogError("error: cmd=getthumbnail kind=syntax");
return false;
@ -295,16 +295,14 @@ bool ChildSession::_handleInput(const char *buffer, int length)
return false;
}
int part = -1;
std::string timestamp, doctemplate;
parseDocOptions(tokens, part, timestamp, doctemplate);
int x, y;
if (!getTokenInteger(tokens[1], "x", x))
x = 0;
assert(!getDocURL().empty());
assert(!getJailedFilePath().empty());
if (!getTokenInteger(tokens[2], "y", y))
y = 0;
bool success = false;
const int x = 0;
const int y = 0;
const int width = 120;
const int height = 120;
const auto mode = static_cast<LibreOfficeKitTileMode>(getLOKitDocument()->getTileMode());
@ -319,13 +317,23 @@ bool ChildSession::_handleInput(const char *buffer, int length)
oss << "sendthumbnail:\n";
// encode PNG to base64
Poco::Base64Encoder encoder(oss);
encoder.rdbuf()->setLineLength(0);
encoder.write(pngThumbnail.data(), pngThumbnail.size());
encoder.close();
try
{
Poco::Base64Encoder encoder(oss);
encoder.rdbuf()->setLineLength(0);
encoder.write(pngThumbnail.data(), pngThumbnail.size());
encoder.close();
std::string sendThumbnailCommand = oss.str();
success = sendTextFrame(sendThumbnailCommand.data(), sendThumbnailCommand.size());
std::string sendThumbnailCommand = oss.str();
success = sendTextFrame(sendThumbnailCommand.data(), sendThumbnailCommand.size());
}
catch (const std::exception& e)
{
LOG_ERR("Encoding thumbnail failed: " << e.what());
std::string error = "sendthumbnail: error";
sendTextFrame(error.data(), error.size());
success = false;
}
}
return success;

View file

@ -77,7 +77,8 @@ ClientSession::ClientSession(
_tileHeightTwips(0),
_kitViewId(-1),
_serverURL(requestDetails),
_isTextDocument(false)
_isTextDocument(false),
_thumbnailSession(false)
{
const std::size_t curConnections = ++COOLWSD::NumConnections;
LOG_INF("ClientSession ctor [" << getName() << "] for URI: [" << _uriPublic.toString()
@ -1923,6 +1924,28 @@ bool ClientSession::handleKitToClientMessage(const std::shared_ptr<Message>& pay
Admin::instance().setViewLoadDuration(docBroker->getDocKey(), getId(), std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - _viewLoadStart));
#endif
// position cursor for thumbnail rendering
if (_thumbnailSession)
{
//check whether we have a target!
std::ostringstream cmd;
cmd << "{";
cmd << "\"Name\":"
"{"
"\"type\":\"string\","
"\"value\":\"URL\""
"},"
"\"URL\":"
"{"
"\"type\":\"string\","
"\"value\":\"#";
cmd << getThumbnailTarget();
cmd << "\"}}";
const std::string renderThumbnailCmd = "uno .uno:OpenHyperLink " + cmd.str();
docBroker->forwardToChild(client_from_this(), renderThumbnailCmd);
}
// Wopi post load actions
if (_wopiFileInfo && !_wopiFileInfo->getTemplateSource().empty())
{
@ -2022,6 +2045,25 @@ bool ClientSession::handleKitToClientMessage(const std::shared_ptr<Message>& pay
}
docBroker->invalidateCursor(x, y, w, h);
// session used for thumbnailing and target already was set
if (_thumbnailSession)
{
bool cursorAlreadyAtTargetPosition = getThumbnailTarget().empty();
if (cursorAlreadyAtTargetPosition)
{
std::ostringstream renderThumbnailCmd;
renderThumbnailCmd << "getthumbnail x=" << x << " y=" << y;
docBroker->forwardToChild(client_from_this(), renderThumbnailCmd.str());
}
else
{
// this is initial cursor position message
// wait for second invalidatecursor message
// reset target so we will proceed next time
setThumbnailTarget(std::string());
}
}
}
else
{
@ -2077,35 +2119,47 @@ bool ClientSession::handleKitToClientMessage(const std::shared_ptr<Message>& pay
else if (tokens.equals(0, "sendthumbnail:"))
{
LOG_TRC("Sending get-thumbnail response.");
bool error = false;
std::ostringstream oss;
oss << "HTTP/1.1 200 OK\r\n"
"Last-Modified: " << Util::getHttpTimeNow() << "\r\n"
"User-Agent: " WOPI_AGENT_STRING "\r\n"
"Content-Type: image/png\r\n"
"X-Content-Type-Options: nosniff\r\n"
"\r\n";
if (firstLine.find("error") != std::string::npos)
error = true;
int firstLineSize = firstLine.size() + 1;
std::string base64thumbnail(payload->data().data() + firstLineSize);
if (!error)
{
int firstLineSize = firstLine.size() + 1;
std::string base64thumbnail(payload->data().data() + firstLineSize, payload->data().size() - firstLineSize);
// decode back to PNG
std::istringstream istr(base64thumbnail);
std::ostringstream ostr;
Poco::Base64Decoder b64in(istr);
copy(std::istreambuf_iterator<char>(b64in),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(ostr));
// decode back to PNG
try
{
std::istringstream istr(base64thumbnail);
std::ostringstream ostr;
Poco::Base64Decoder b64in(istr);
copy(std::istreambuf_iterator<char>(b64in),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(ostr));
oss << ostr.str();
http::Response httpResponse(http::StatusCode::OK);
httpResponse.set("Last-Modified", Util::getHttpTimeNow());
httpResponse.set("X-Content-Type-Options", "nosniff");
httpResponse.setBody(ostr.str(), "image/png");
_saveAsSocket->sendAndShutdown(httpResponse);
}
catch (const std::exception& e)
{
LOG_ERR("Decoding thumbnail failed: " << e.what());
error = true;
}
}
_saveAsSocket->send(oss.str());
_saveAsSocket->shutdown();
if (error)
{
http::Response httpResponse(http::StatusCode::InternalServerError);
httpResponse.set("Content-Length", "0");
_saveAsSocket->sendAndShutdown(httpResponse);
}
LOG_TRC("Removing get-thumbnail ClientSession.");
docBroker->removeSession(client_from_this());
docBroker->closeDocument("ownertermination");
docBroker->closeDocument("thumbnailgenerated");
}
}
else

View file

@ -212,6 +212,14 @@ public:
bool isTextDocument() const { return _isTextDocument; }
void setThumbnailSession(const bool val) { _thumbnailSession = val; }
void setThumbnailTarget(const std::string& target) { _thumbnailTarget = target; }
const std::string& getThumbnailTarget() const { return _thumbnailTarget; }
bool thumbnailSession() { return _thumbnailSession; }
/// Do we recognize this clipboard ?
bool matchesClipboardKeys(const std::string &viewId, const std::string &tag);
@ -362,6 +370,12 @@ private:
/// Client is using a text document?
bool _isTextDocument;
/// Session used to generate thumbnail
bool _thumbnailSession;
/// Target used for thumbnail rendering
std::string _thumbnailTarget;
/// Rotating clipboard remote access identifiers - protected by GlobalSessionMapMutex
std::string _clipboardKeys[2];

View file

@ -3683,12 +3683,10 @@ void ExtractLinkTargetsBroker::sendStartMessage(std::shared_ptr<ClientSession> c
void GetThumbnailBroker::sendStartMessage(std::shared_ptr<ClientSession> clientSession, const std::string& encodedFrom)
{
// load first
ConvertToBroker::sendStartMessage(clientSession, encodedFrom);
clientSession->setThumbnailSession(true);
clientSession->setThumbnailTarget(_target);
auto docBroker = this;
const auto command = "getthumbnail url=" + encodedFrom + " target=" + _target;
docBroker->forwardToChild(clientSession, command);
ConvertToBroker::sendStartMessage(clientSession, encodedFrom);
}
void ConvertToBroker::dispose()