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:
parent
3bec642b02
commit
e82aa05293
4 changed files with 117 additions and 43 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue