LibreOffice Module desktop (master)  1
init.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <config_features.h>
11 
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #ifdef IOS
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <unicode/udata.h>
20 #include <unicode/ucnv.h>
21 #include <premac.h>
22 #import <Foundation/Foundation.h>
23 #import <CoreGraphics/CoreGraphics.h>
24 #include <postmac.h>
25 #endif
26 
27 #ifdef ANDROID
28 #include <osl/detail/android-bootstrap.h>
29 #endif
30 
31 #include <algorithm>
32 #include <memory>
33 #include <iostream>
34 #include <boost/property_tree/json_parser.hpp>
35 #include <boost/algorithm/string.hpp>
36 
37 #include <LibreOfficeKit/LibreOfficeKit.h>
38 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
39 
40 #include <sal/log.hxx>
41 #include <vcl/errinf.hxx>
42 #include <vcl/lok.hxx>
43 #include <o3tl/any.hxx>
44 #include <osl/file.hxx>
45 #include <osl/process.h>
46 #include <osl/thread.h>
47 #include <rtl/bootstrap.hxx>
48 #include <rtl/strbuf.hxx>
49 #include <rtl/uri.hxx>
50 #include <svl/zforlist.hxx>
51 #include <cppuhelper/bootstrap.hxx>
52 #include <comphelper/base64.hxx>
54 #include <comphelper/lok.hxx>
56 #include <comphelper/string.hxx>
61 
62 #include <com/sun/star/beans/XPropertySet.hpp>
63 #include <com/sun/star/container/XNameAccess.hpp>
64 #include <com/sun/star/frame/Desktop.hpp>
65 #include <com/sun/star/frame/DispatchResultEvent.hpp>
66 #include <com/sun/star/frame/DispatchResultState.hpp>
67 #include <com/sun/star/frame/XDispatchProvider.hpp>
68 #include <com/sun/star/frame/XDispatchResultListener.hpp>
69 #include <com/sun/star/frame/XSynchronousDispatch.hpp>
70 #include <com/sun/star/frame/XStorable.hpp>
71 #include <com/sun/star/lang/Locale.hpp>
72 #include <com/sun/star/lang/XComponent.hpp>
73 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
74 #include <com/sun/star/reflection/theCoreReflection.hpp>
75 #include <com/sun/star/reflection/XIdlClass.hpp>
76 #include <com/sun/star/reflection/XIdlReflection.hpp>
77 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
78 #include <com/sun/star/util/URLTransformer.hpp>
79 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
80 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
81 #include <com/sun/star/datatransfer/XTransferable2.hpp>
82 #include <com/sun/star/text/TextContentAnchorType.hpp>
83 #include <com/sun/star/document/XRedlinesSupplier.hpp>
84 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
85 
86 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
87 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
88 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
89 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
90 #include <com/sun/star/security/XCertificate.hpp>
91 
92 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
93 #include <com/sun/star/linguistic2/XSpellChecker.hpp>
94 #include <com/sun/star/i18n/ScriptType.hpp>
95 #include <com/sun/star/lang/DisposedException.hpp>
96 
97 #include <editeng/flstitem.hxx>
98 #ifdef IOS
99 #include <sfx2/app.hxx>
100 #endif
101 #include <sfx2/objsh.hxx>
102 #include <sfx2/viewsh.hxx>
103 #include <sfx2/viewfrm.hxx>
104 #include <sfx2/msgpool.hxx>
105 #include <sfx2/dispatch.hxx>
106 #include <sfx2/lokcharthelper.hxx>
107 #include <sfx2/DocumentSigner.hxx>
109 #include <svx/dialmgr.hxx>
110 #include <svx/strings.hrc>
111 #include <svx/svdview.hxx>
112 #include <svx/svxids.hrc>
113 #include <svx/ucsubset.hxx>
114 #include <vcl/vclevent.hxx>
115 #include <vcl/GestureEvent.hxx>
116 #include <vcl/svapp.hxx>
117 #include <unotools/resmgr.hxx>
118 #include <tools/fract.hxx>
119 #include <svtools/ctrltool.hxx>
120 #include <svtools/langtab.hxx>
121 #include <vcl/floatwin.hxx>
122 #include <vcl/fontcharmap.hxx>
123 #include <vcl/graphicfilter.hxx>
124 #ifdef IOS
125 #include <vcl/sysdata.hxx>
126 #endif
127 #include <vcl/virdev.hxx>
128 #include <vcl/ImageTree.hxx>
129 #include <vcl/ITiledRenderable.hxx>
130 #include <vcl/dialoghelper.hxx>
131 #include <unicode/uchar.h>
134 #include <unotools/pathoptions.hxx>
135 #include <unotools/tempfile.hxx>
136 #include <unotools/streamwrap.hxx>
137 #include <osl/module.hxx>
138 #include <comphelper/sequence.hxx>
139 #include <sfx2/sfxbasemodel.hxx>
140 #include <svl/undo.hxx>
141 #include <unotools/datetime.hxx>
142 #include <i18nlangtag/mslangid.hxx>
144 #include <vcl/builder.hxx>
145 #include <vcl/abstdlg.hxx>
146 #include <tools/diagnose_ex.h>
147 #include <vcl/uitest/uiobject.hxx>
148 
149 // Needed for getUndoManager()
150 #include <com/sun/star/document/XUndoManager.hpp>
151 #include <com/sun/star/document/XUndoManagerSupplier.hpp>
152 #include <editeng/sizeitem.hxx>
153 #include <svx/rulritem.hxx>
154 #include <svx/pageitem.hxx>
155 
156 #include <app.hxx>
157 
158 #include "../app/cmdlineargs.hxx"
159 // We also need to hackily be able to start the main libreoffice thread:
160 #include "../app/sofficemain.h"
161 #include "../app/officeipcthread.hxx"
162 #include <lib/init.hxx>
163 
164 #include "lokinteractionhandler.hxx"
165 #include "lokclipboard.hxx"
166 #include <officecfg/Office/Impress.hxx>
167 
168 using namespace css;
169 using namespace vcl;
170 using namespace desktop;
171 using namespace utl;
172 
173 static LibLibreOffice_Impl *gImpl = nullptr;
174 static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
175 static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
176 
177 static void SetLastExceptionMsg(const OUString& s = OUString())
178 {
179  SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
180  if (gImpl)
181  gImpl->maLastExceptionMsg = s;
182 }
183 
184 namespace {
185 
186 struct ExtensionMap
187 {
188  const char *extn;
189  const char *filterName;
190 };
191 
192 }
193 
194 static const ExtensionMap aWriterExtensionMap[] =
195 {
196  { "doc", "MS Word 97" },
197  { "docm", "MS Word 2007 XML VBA" },
198  { "docx", "MS Word 2007 XML" },
199  { "fodt", "OpenDocument Text Flat XML" },
200  { "html", "HTML (StarWriter)" },
201  { "odt", "writer8" },
202  { "ott", "writer8_template" },
203  { "pdf", "writer_pdf_Export" },
204  { "epub", "EPUB" },
205  { "rtf", "Rich Text Format" },
206  { "txt", "Text" },
207  { "xhtml", "XHTML Writer File" },
208  { "png", "writer_png_Export" },
209  { nullptr, nullptr }
210 };
211 
212 static const ExtensionMap aCalcExtensionMap[] =
213 {
214  { "csv", "Text - txt - csv (StarCalc)" },
215  { "fods", "OpenDocument Spreadsheet Flat XML" },
216  { "html", "HTML (StarCalc)" },
217  { "ods", "calc8" },
218  { "ots", "calc8_template" },
219  { "pdf", "calc_pdf_Export" },
220  { "xhtml", "XHTML Calc File" },
221  { "xls", "MS Excel 97" },
222  { "xlsm", "Calc MS Excel 2007 VBA XML" },
223  { "xlsx", "Calc MS Excel 2007 XML" },
224  { "png", "calc_png_Export" },
225  { nullptr, nullptr }
226 };
227 
228 static const ExtensionMap aImpressExtensionMap[] =
229 {
230  { "fodp", "OpenDocument Presentation Flat XML" },
231  { "html", "impress_html_Export" },
232  { "odg", "impress8_draw" },
233  { "odp", "impress8" },
234  { "otp", "impress8_template" },
235  { "pdf", "impress_pdf_Export" },
236  { "potm", "Impress MS PowerPoint 2007 XML Template" },
237  { "pot", "MS PowerPoint 97 Vorlage" },
238  { "pptm", "Impress MS PowerPoint 2007 XML VBA" },
239  { "pptx", "Impress MS PowerPoint 2007 XML" },
240  { "pps", "MS PowerPoint 97 Autoplay" },
241  { "ppt", "MS PowerPoint 97" },
242  { "svg", "impress_svg_Export" },
243  { "swf", "impress_flash_Export" },
244  { "xhtml", "XHTML Impress File" },
245  { "png", "impress_png_Export"},
246  { nullptr, nullptr }
247 };
248 
249 static const ExtensionMap aDrawExtensionMap[] =
250 {
251  { "fodg", "draw_ODG_FlatXML" },
252  { "html", "draw_html_Export" },
253  { "odg", "draw8" },
254  { "pdf", "draw_pdf_Export" },
255  { "svg", "draw_svg_Export" },
256  { "swf", "draw_flash_Export" },
257  { "xhtml", "XHTML Draw File" },
258  { "png", "draw_png_Export"},
259  { nullptr, nullptr }
260 };
261 
262 static OUString getUString(const char* pString)
263 {
264  if (pString == nullptr)
265  return OUString();
266 
267  OString sString(pString, strlen(pString));
268  return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
269 }
270 
271 // Tolerate embedded \0s etc.
272 static char *convertOString(const OString &rStr)
273 {
274  char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1));
275  assert(pMemory); // don't tolerate failed allocations.
276  memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1);
277  return pMemory;
278 }
279 
280 static char *convertOUString(const OUString &aStr)
281 {
282  return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
283 }
284 
286 static OUString getAbsoluteURL(const char* pURL)
287 {
288  OUString aURL(getUString(pURL));
289  if (aURL.isEmpty())
290  return aURL;
291 
292  // convert relative paths to absolute ones
293  OUString aWorkingDir;
294  osl_getProcessWorkingDir(&aWorkingDir.pData);
295  if (!aWorkingDir.endsWith("/"))
296  aWorkingDir += "/";
297 
298  try
299  {
300  return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
301  }
302  catch (const rtl::MalformedUriException &)
303  {
304  }
305 
306  return OUString();
307 }
308 
309 static uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree)
310 {
311  uno::Any aAny;
312  uno::Any aValue;
313  sal_Int32 nFields;
314  uno::TypeClass aTypeClass;
315  uno::Reference< reflection::XIdlField > aField;
316  boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField;
317  const std::string& rType = aTree.get<std::string>("type", "");
318  const std::string& rValue = aTree.get<std::string>("value", "");
319  uno::Sequence< uno::Reference< reflection::XIdlField > > aFields;
320  uno::Reference< reflection:: XIdlClass > xIdlClass =
321  css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())->forName(OUString::fromUtf8(rType.c_str()));
322  if (xIdlClass.is())
323  {
324  aTypeClass = xIdlClass->getTypeClass();
325  xIdlClass->createObject(aAny);
326  aFields = xIdlClass->getFields();
327  nFields = aFields.getLength();
328  aNodeValue = aTree.get_child("value", aNodeNull);
329  if (nFields > 0 && aNodeValue != aNodeNull)
330  {
331  for (sal_Int32 itField = 0; itField < nFields; ++itField)
332  {
333  aField = aFields[itField];
334  aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull);
335  if (aNodeField != aNodeNull)
336  {
337  aValue = jsonToUnoAny(aNodeField);
338  aField->set(aAny, aValue);
339  }
340  }
341  }
342  else if (!rValue.empty())
343  {
344  if (aTypeClass == uno::TypeClass_VOID)
345  aAny.clear();
346  else if (aTypeClass == uno::TypeClass_BYTE)
347  aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32());
348  else if (aTypeClass == uno::TypeClass_BOOLEAN)
349  aAny <<= OString(rValue.c_str()).toBoolean();
350  else if (aTypeClass == uno::TypeClass_SHORT)
351  aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32());
352  else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT)
353  aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32());
354  else if (aTypeClass == uno::TypeClass_LONG)
355  aAny <<= OString(rValue.c_str()).toInt32();
356  else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG)
357  aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32());
358  else if (aTypeClass == uno::TypeClass_FLOAT)
359  aAny <<= OString(rValue.c_str()).toFloat();
360  else if (aTypeClass == uno::TypeClass_DOUBLE)
361  aAny <<= OString(rValue.c_str()).toDouble();
362  else if (aTypeClass == uno::TypeClass_STRING)
363  aAny <<= OUString::fromUtf8(rValue.c_str());
364  }
365  }
366  return aAny;
367 }
368 
369 std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
370 {
371  std::vector<beans::PropertyValue> aArguments;
372  if (pJSON && pJSON[0] != '\0')
373  {
374  boost::property_tree::ptree aTree, aNodeNull, aNodeValue;
375  std::stringstream aStream(pJSON);
376  boost::property_tree::read_json(aStream, aTree);
377 
378  for (const auto& rPair : aTree)
379  {
380  const std::string& rType = rPair.second.get<std::string>("type", "");
381  const std::string& rValue = rPair.second.get<std::string>("value", "");
382 
383  beans::PropertyValue aValue;
384  aValue.Name = OUString::fromUtf8(rPair.first.c_str());
385  if (rType == "string")
386  aValue.Value <<= OUString::fromUtf8(rValue.c_str());
387  else if (rType == "boolean")
388  aValue.Value <<= OString(rValue.c_str()).toBoolean();
389  else if (rType == "float")
390  aValue.Value <<= OString(rValue.c_str()).toFloat();
391  else if (rType == "long")
392  aValue.Value <<= OString(rValue.c_str()).toInt32();
393  else if (rType == "short")
394  aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
395  else if (rType == "unsigned short")
396  aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
397  else if (rType == "int64")
398  aValue.Value <<= OString(rValue.c_str()).toInt64();
399  else if (rType == "int32")
400  aValue.Value <<= OString(rValue.c_str()).toInt32();
401  else if (rType == "int16")
402  aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
403  else if (rType == "uint64")
404  aValue.Value <<= OString(rValue.c_str()).toUInt64();
405  else if (rType == "uint32")
406  aValue.Value <<= OString(rValue.c_str()).toUInt32();
407  else if (rType == "uint16")
408  aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
409  else if (rType == "[]byte")
410  {
411  aNodeValue = rPair.second.get_child("value", aNodeNull);
412  if (aNodeValue != aNodeNull && aNodeValue.size() == 0)
413  {
414  uno::Sequence< sal_Int8 > aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), rValue.size());
415  aValue.Value <<= aSeqByte;
416  }
417  }
418  else if (rType == "[]any")
419  {
420  aNodeValue = rPair.second.get_child("value", aNodeNull);
421  if (aNodeValue != aNodeNull && !aNodeValue.empty())
422  {
423  sal_Int32 itSeq = 0;
424  uno::Sequence< uno::Any > aSeq(aNodeValue.size());
425  for (const auto& rSeqPair : aNodeValue)
426  aSeq[itSeq++] = jsonToUnoAny(rSeqPair.second);
427  aValue.Value <<= aSeq;
428  }
429  }
430  else
431  SAL_WARN("desktop.lib", "jsonToPropertyValuesVector: unhandled type '"<<rType<<"'");
432  aArguments.push_back(aValue);
433  }
434  }
435  return aArguments;
436 }
437 
438 
439 static StringMap jsonToStringMap(const char* pJSON)
440 {
441  StringMap aArgs;
442  if (pJSON && pJSON[0] != '\0')
443  {
444  std::stringstream aStream(pJSON);
445  boost::property_tree::ptree aTree;
446  boost::property_tree::read_json(aStream, aTree);
447 
448  for (const auto& rPair : aTree)
449  {
450  aArgs[OUString::fromUtf8(rPair.first.c_str())] = OUString::fromUtf8(rPair.second.get_value<std::string>(".").c_str());
451  }
452  }
453  return aArgs;
454 }
455 
456 
457 static boost::property_tree::ptree unoAnyToPropertyTree(const uno::Any& anyItem)
458 {
459  boost::property_tree::ptree aTree;
460  OUString aType = anyItem.getValueTypeName();
461  aTree.put("type", aType.toUtf8().getStr());
462 
463  if (aType == "string")
464  aTree.put("value", anyItem.get<OUString>().toUtf8().getStr());
465  else if (aType == "unsigned long")
466  aTree.put("value", OString::number(anyItem.get<sal_uInt32>()).getStr());
467  else if (aType == "long")
468  aTree.put("value", OString::number(anyItem.get<sal_Int32>()).getStr());
469  else if (aType == "[]any")
470  {
471  uno::Sequence<uno::Any> aSeq;
472  if (anyItem >>= aSeq)
473  {
474  boost::property_tree::ptree aSubTree;
475 
476  for (auto i = 0; i < aSeq.getLength(); ++i)
477  {
478  aSubTree.add_child(OString::number(i).getStr(), unoAnyToPropertyTree(aSeq[i]));
479  }
480  aTree.add_child("value", aSubTree);
481  }
482  }
483 
484  // TODO: Add more as required
485 
486  return aTree;
487 }
488 
489 namespace desktop {
490 
491 RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
492 {
493  RectangleAndPart aRet;
494  if (rPayload.compare(0, 5, "EMPTY") == 0) // payload starts with "EMPTY"
495  {
498  aRet.m_nPart = std::stol(rPayload.substr(6));
499 
500  return aRet;
501  }
502 
503  std::istringstream aStream(rPayload);
504  long nLeft, nTop, nWidth, nHeight;
505  long nPart = INT_MIN;
506  char nComma;
508  {
509  aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight >> nComma >> nPart;
510  }
511  else
512  {
513  aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
514  }
515 
516  if (nWidth > 0 && nHeight > 0)
517  {
518  // The top-left corner starts at (0, 0).
519  // Anything negative is invalid.
520  if (nLeft < 0)
521  {
522  nWidth += nLeft;
523  nLeft = 0;
524  }
525 
526  if (nTop < 0)
527  {
528  nHeight += nTop;
529  nTop = 0;
530  }
531 
532  if (nWidth > 0 && nHeight > 0)
533  {
534  aRet.m_aRectangle = tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
535  }
536  }
537  // else leave empty rect.
538 
539  aRet.m_nPart = nPart;
540  return aRet;
541 }
542 
543 RectangleAndPart& CallbackFlushHandler::CallbackData::setRectangleAndPart(const std::string& payload)
544 {
545  setRectangleAndPart(RectangleAndPart::Create(payload));
546 
547  // Return reference to the cached object.
548  return boost::get<RectangleAndPart>(PayloadObject);
549 }
550 
551 void CallbackFlushHandler::CallbackData::setRectangleAndPart(const RectangleAndPart& rRectAndPart)
552 {
553  PayloadString = rRectAndPart.toString().getStr();
554  PayloadObject = rRectAndPart;
555 }
556 
557 const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const
558 {
559  assert(PayloadObject.which() == 1);
560  return boost::get<RectangleAndPart>(PayloadObject);
561 }
562 
563 boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
564 {
565  boost::property_tree::ptree aTree;
566  std::stringstream aStream(payload);
567  boost::property_tree::read_json(aStream, aTree);
568 
569  // Let boost normalize the payload so it always matches the cache.
570  setJson(aTree);
571 
572  // Return reference to the cached object.
573  return boost::get<boost::property_tree::ptree>(PayloadObject);
574 }
575 
576 void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
577 {
578  std::stringstream aJSONStream;
579  constexpr bool bPretty = false; // Don't waste time and bloat logs.
580  boost::property_tree::write_json(aJSONStream, rTree, bPretty);
581  PayloadString = boost::trim_copy(aJSONStream.str());
582 
583  PayloadObject = rTree;
584 }
585 
586 const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
587 {
588  assert(PayloadObject.which() == 2);
589  return boost::get<boost::property_tree::ptree>(PayloadObject);
590 }
591 
592 bool CallbackFlushHandler::CallbackData::validate() const
593 {
594  switch (PayloadObject.which())
595  {
596  // Not cached.
597  case 0:
598  return true;
599 
600  // RectangleAndPart.
601  case 1:
602  return getRectangleAndPart().toString().getStr() == PayloadString;
603 
604  // Json.
605  case 2:
606  {
607  std::stringstream aJSONStream;
608  boost::property_tree::write_json(aJSONStream, getJson(), false);
609  const std::string aExpected = boost::trim_copy(aJSONStream.str());
610  return aExpected == PayloadString;
611  }
612 
613  default:
614  assert(!"Unknown variant type; please add an entry to validate.");
615  }
616 
617  return false;
618 }
619 
620 }
621 
622 namespace {
623 
624 bool lcl_isViewCallbackType(const int type)
625 {
626  switch (type)
627  {
628  case LOK_CALLBACK_CELL_VIEW_CURSOR:
629  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
630  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
631  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
632  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
633  return true;
634 
635  default:
636  return false;
637  }
638 }
639 
640 int lcl_getViewId(const std::string& payload)
641 {
642  // this is a cheap way how to get the viewId from a JSON message; proper
643  // parsing is terribly expensive, and we just need the viewId here
644  size_t viewIdPos = payload.find("viewId");
645  if (viewIdPos == std::string::npos)
646  return 0;
647 
648  size_t numberPos = payload.find(":", viewIdPos + 6);
649  if (numberPos == std::string::npos)
650  return 0;
651 
652  for (++numberPos; numberPos < payload.length(); ++numberPos)
653  {
654  if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
655  break;
656  }
657 
658  if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
659  return strtol(payload.substr(numberPos).c_str(), nullptr, 10);
660 
661  return 0;
662 }
663 
664 int lcl_getViewId(const desktop::CallbackFlushHandler::CallbackData& rCallbackData)
665 {
666  if (rCallbackData.isCached())
667  return rCallbackData.getJson().get<int>("viewId");
668  return lcl_getViewId(rCallbackData.PayloadString);
669 }
670 
671 std::string extractCertificate(const std::string & certificate)
672 {
673  const std::string header("-----BEGIN CERTIFICATE-----");
674  const std::string footer("-----END CERTIFICATE-----");
675 
676  std::string result;
677 
678  size_t pos1 = certificate.find(header);
679  if (pos1 == std::string::npos)
680  return result;
681 
682  size_t pos2 = certificate.find(footer, pos1 + 1);
683  if (pos2 == std::string::npos)
684  return result;
685 
686  pos1 = pos1 + header.length();
687  pos2 = pos2 - pos1;
688 
689  return certificate.substr(pos1, pos2);
690 }
691 
692 std::string extractPrivateKey(const std::string & privateKey)
693 {
694  const std::string header("-----BEGIN PRIVATE KEY-----");
695  const std::string footer("-----END PRIVATE KEY-----");
696 
697  std::string result;
698 
699  size_t pos1 = privateKey.find(header);
700  if (pos1 == std::string::npos)
701  return result;
702 
703  size_t pos2 = privateKey.find(footer, pos1 + 1);
704  if (pos2 == std::string::npos)
705  return result;
706 
707  pos1 = pos1 + header.length();
708  pos2 = pos2 - pos1;
709 
710  return privateKey.substr(pos1, pos2);
711 }
712 
713 // Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation
714 css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame )
715 {
716  const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController();
717  if ( xController.is() )
718  {
719  const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel();
720  if ( xModel.is() )
721  {
722  const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW );
723  return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW );
724  }
725  }
726 
727  return css::uno::Reference< css::document::XUndoManager > ();
728 }
729 
730 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
731 void ExecuteMarginLRChange(
732  const long nPageLeftMargin,
733  const long nPageRightMargin,
734  SvxLongLRSpaceItem* pPageLRMarginItem)
735 {
736  pPageLRMarginItem->SetLeft( nPageLeftMargin );
737  pPageLRMarginItem->SetRight( nPageRightMargin );
738  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE,
739  SfxCallMode::RECORD, { pPageLRMarginItem });
740 }
741 
742 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
743 void ExecuteMarginULChange(
744  const long nPageTopMargin,
745  const long nPageBottomMargin,
746  SvxLongULSpaceItem* pPageULMarginItem)
747 {
748  pPageULMarginItem->SetUpper( nPageTopMargin );
749  pPageULMarginItem->SetLower( nPageBottomMargin );
750  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE,
751  SfxCallMode::RECORD, { pPageULMarginItem });
752 }
753 
754 // Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation
755 void ExecuteOrientationChange()
756 {
757  std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE));
758  std::unique_ptr<SvxSizeItem> pPageSizeItem(new SvxSizeItem(SID_ATTR_PAGE_SIZE));
759  std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ));
760  std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ));
761  // 1mm in twips rounded
762  // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx
763  const long MINBODY = 56;
764  bool bIsLandscape = false;
765 
766  css::uno::Reference< css::document::XUndoManager > mxUndoManager(
767  getUndoManager( SfxViewFrame::Current()->GetFrame().GetFrameInterface() ) );
768 
769  if ( mxUndoManager.is() )
770  mxUndoManager->enterUndoContext( "" );
771 
772 
773  const SfxPoolItem* pItem;
774 
775 
776  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pItem);
777  pPageSizeItem.reset( static_cast<SvxSizeItem*>(pItem->Clone()) );
778 
779 
780 
781  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pItem);
782  pPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pItem->Clone()) );
783 
784 
785 
786  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pItem);
787  pPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pItem->Clone()) );
788 
789 
790  {
791  if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height())
792  bIsLandscape = true;
793 
794  // toggle page orientation
795  pPageItem->SetLandscape(!bIsLandscape);
796 
797 
798  // swap the width and height of the page size
799  const long nRotatedWidth = pPageSizeItem->GetSize().Height();
800  const long nRotatedHeight = pPageSizeItem->GetSize().Width();
801  pPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight));
802 
803 
804  // apply changed attributes
805  if (SfxViewShell::Current())
806  {
807  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE,
808  SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() });
809  }
810  }
811 
812 
813  // check, if margin values still fit to the changed page size.
814  // if not, adjust margin values
815  {
816  const long nML = pPageLRMarginItem->GetLeft();
817  const long nMR = pPageLRMarginItem->GetRight();
818  const long nTmpPW = nML + nMR + MINBODY;
819 
820  const long nPW = pPageSizeItem->GetSize().Width();
821 
822  if ( nTmpPW > nPW )
823  {
824  if ( nML <= nMR )
825  {
826  ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() );
827  }
828  else
829  {
830  ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() );
831  }
832  }
833 
834  const long nMT = pPageULMarginItem->GetUpper();
835  const long nMB = pPageULMarginItem->GetLower();
836  const long nTmpPH = nMT + nMB + MINBODY;
837 
838  const long nPH = pPageSizeItem->GetSize().Height();
839 
840  if ( nTmpPH > nPH )
841  {
842  if ( nMT <= nMB )
843  {
844  ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() );
845  }
846  else
847  {
848  ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() );
849  }
850  }
851  }
852 
853  if ( mxUndoManager.is() )
854  mxUndoManager->leaveUndoContext();
855 }
856 
857 void setupSidebar(bool bShow)
858 {
859  SfxViewShell* pViewShell = SfxViewShell::Current();
860  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
861  if (pViewFrame)
862  {
863  if (bShow && !pViewFrame->GetChildWindow(SID_SIDEBAR))
864  pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */);
865 
866  pViewFrame->ShowChildWindow(SID_SIDEBAR, bShow);
867 
868  if (!bShow)
869  return;
870 
871  // Force synchronous population of panels
872  SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
873  if (!pChild)
874  return;
875 
876  auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow());
877  if (!pDockingWin)
878  return;
879  pDockingWin->SyncUpdate();
880  }
881  else
882  SetLastExceptionMsg("No view shell or sidebar");
883 }
884 
885 VclPtr<Window> getSidebarWindow()
886 {
887  VclPtr<Window> xRet;
888 
889  setupSidebar(true);
890  SfxViewShell* pViewShell = SfxViewShell::Current();
891  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
892  if (!pViewFrame)
893  return xRet;
894 
895  // really a SidebarChildWindow
896  SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
897  if (!pChild)
898  return xRet;
899 
900  // really a SidebarDockingWindow
901  vcl::Window *pWin = pChild->GetWindow();
902  if (!pWin)
903  return xRet;
904  xRet = pWin;
905  return xRet;
906 }
907 
908 } // end anonymous namespace
909 
910 // Could be anonymous in principle, but for the unit testing purposes, we
911 // declare it in init.hxx.
912 OUString desktop::extractParameter(OUString& rOptions, const OUString& rName)
913 {
914  OUString aValue;
915 
916  OUString aNameEquals(rName + "=");
917  OUString aCommaNameEquals("," + rName + "=");
918 
919  int nIndex = -1;
920  if (rOptions.startsWith(aNameEquals))
921  {
922  size_t nLen = aNameEquals.getLength();
923  int nComma = rOptions.indexOf(",", nLen);
924  if (nComma >= 0)
925  {
926  aValue = rOptions.copy(nLen, nComma - nLen);
927  rOptions = rOptions.copy(nComma + 1);
928  }
929  else
930  {
931  aValue = rOptions.copy(nLen);
932  rOptions.clear();
933  }
934  }
935  else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
936  {
937  size_t nLen = aCommaNameEquals.getLength();
938  int nComma = rOptions.indexOf(",", nIndex + nLen);
939  if (nComma >= 0)
940  {
941  aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
942  rOptions = rOptions.copy(0, nIndex) + rOptions.copy(nComma);
943  }
944  else
945  {
946  aValue = rOptions.copy(nIndex + nLen);
947  rOptions = rOptions.copy(0, nIndex);
948  }
949  }
950 
951  return aValue;
952 }
953 
954 extern "C"
955 {
956 
957 static void doc_destroy(LibreOfficeKitDocument* pThis);
958 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
959 static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
960 static int doc_getParts(LibreOfficeKitDocument* pThis);
961 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
962 static int doc_getPart(LibreOfficeKitDocument* pThis);
963 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
964 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
965 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
966 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
967 static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
968 static void doc_paintTile(LibreOfficeKitDocument* pThis,
969  unsigned char* pBuffer,
970  const int nCanvasWidth, const int nCanvasHeight,
971  const int nTilePosX, const int nTilePosY,
972  const int nTileWidth, const int nTileHeight);
973 #ifdef IOS
974 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
975  void* rCGContext,
976  const int nCanvasWidth, const int nCanvasHeight,
977  const int nTilePosX, const int nTilePosY,
978  const int nTileWidth, const int nTileHeight);
979 #endif
980 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
981  unsigned char* pBuffer,
982  const int nPart,
983  const int nCanvasWidth, const int nCanvasHeight,
984  const int nTilePosX, const int nTilePosY,
985  const int nTileWidth, const int nTileHeight);
986 static int doc_getTileMode(LibreOfficeKitDocument* pThis);
987 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
988  long* pWidth,
989  long* pHeight);
990 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
991  const char* pArguments);
992 
993 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
994  LibreOfficeKitCallback pCallback,
995  void* pData);
996 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
997  int nType,
998  int nCharCode,
999  int nKeyCode);
1000 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
1001  unsigned nWindowId,
1002  int nType,
1003  const char* pText);
1004 static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
1005  unsigned nLOKWindowId,
1006  int nCharBefore,
1007  int nCharAfter);
1008 static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
1009  unsigned nLOKWindowId,
1010  const char* pArguments);
1011 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
1012  unsigned nLOKWindowId,
1013  int nType,
1014  int nCharCode,
1015  int nKeyCode);
1016 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
1017  int nType,
1018  int nX,
1019  int nY,
1020  int nCount,
1021  int nButtons,
1022  int nModifier);
1023 static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis,
1024  unsigned nLOKWindowId,
1025  int nType,
1026  int nX,
1027  int nY,
1028  int nCount,
1029  int nButtons,
1030  int nModifier);
1031 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
1032  unsigned nLOKWindowId,
1033  const char* pType,
1034  int nX,
1035  int nY,
1036  int nOffset);
1037 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
1038  const char* pCommand,
1039  const char* pArguments,
1040  bool bNotifyWhenFinished);
1041 static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis,
1042  unsigned nLOKWindowId,
1043  bool swap,
1044  int nX,
1045  int nY);
1046 static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
1047  int nType,
1048  int nX,
1049  int nY);
1050 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
1051  const char* pMimeType,
1052  char** pUsedMimeType);
1053 static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
1054 static int doc_getClipboard (LibreOfficeKitDocument* pThis,
1055  const char **pMimeTypes,
1056  size_t *pOutCount,
1057  char ***pOutMimeTypes,
1058  size_t **pOutSizes,
1059  char ***pOutStreams);
1060 static int doc_setClipboard (LibreOfficeKitDocument* pThis,
1061  const size_t nInCount,
1062  const char **pInMimeTypes,
1063  const size_t *pInSizes,
1064  const char **pInStreams);
1065 static bool doc_paste(LibreOfficeKitDocument* pThis,
1066  const char* pMimeType,
1067  const char* pData,
1068  size_t nSize);
1069 static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
1070  int nType,
1071  int nX,
1072  int nY);
1073 static void doc_resetSelection (LibreOfficeKitDocument* pThis);
1074 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
1075 static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
1076  int nTilePixelWidth,
1077  int nTilePixelHeight,
1078  int nTileTwipWidth,
1079  int nTileTwipHeight);
1080 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
1081 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
1082 static int doc_createView(LibreOfficeKitDocument* pThis);
1083 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
1084 static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
1085 static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
1086 static int doc_getView(LibreOfficeKitDocument* pThis);
1087 static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
1088 static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
1089 static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
1090 static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
1091  const char *pFontName,
1092  const char *pChar,
1093  int* pFontWidth,
1094  int* pFontHeight,
1095  int pOrientation);
1096 static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
1097  const char *pFontName,
1098  const char *pChar,
1099  int* pFontWidth,
1100  int* pFontHeight);
1101 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
1102 
1103 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1104  const int nX, const int nY,
1105  const int nWidth, const int nHeight);
1106 
1107 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1108  const int nX, const int nY,
1109  const int nWidth, const int nHeight,
1110  const double fDPIScale);
1111 
1112 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1113  const int nX, const int nY,
1114  const int nWidth, const int nHeight,
1115  const double fDPIScale, int viewId);
1116 
1117 static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
1118  nLOKWindowId, int nAction, const char* pData);
1119 
1120 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
1121 
1122 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
1123  const unsigned char* pCertificateBinary,
1124  const int nCertificateBinarySize,
1125  const unsigned char* pPrivateKeyBinary,
1126  const int nPrivateKeyBinarySize);
1127 
1128 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
1129  const unsigned char* pCertificateBinary,
1130  const int nCertificateBinarySize);
1131 
1132 static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
1133 
1134 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
1135 
1136 static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
1137  const int nWidth, const int nHeight);
1138 
1139 static void doc_completeFunction(LibreOfficeKitDocument* pThis, int nIndex);
1140 } // extern "C"
1141 
1142 namespace {
1143 ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
1144 {
1145  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1146  return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
1147 }
1148 
1149 #ifndef IOS
1150 
1151 /*
1152  * Unfortunately clipboard creation using UNO is insanely baroque.
1153  * we also need to ensure that this works for the first view which
1154  * has no clear 'createView' called for it (unfortunately).
1155  */
1156 rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
1157 {
1158  ITiledRenderable* pDoc = getTiledRenderable(pThis);
1160 
1161  SAL_INFO("lok", "Set to clipboard for view " << xClip.get());
1162  // FIXME: using a hammer here - should not be necessary if all tests used createView.
1163  pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY));
1164 
1165  return xClip;
1166 }
1167 
1168 #endif
1169 
1170 } // anonymous namespace
1171 
1172 LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent)
1173  : mxComponent(xComponent)
1174 {
1175  if (!(m_pDocumentClass = gDocumentClass.lock()))
1176  {
1177  m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>();
1178 
1179  m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
1180 
1181  m_pDocumentClass->destroy = doc_destroy;
1182  m_pDocumentClass->saveAs = doc_saveAs;
1183  m_pDocumentClass->getDocumentType = doc_getDocumentType;
1184  m_pDocumentClass->getParts = doc_getParts;
1185  m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles;
1186  m_pDocumentClass->getPart = doc_getPart;
1187  m_pDocumentClass->setPart = doc_setPart;
1188  m_pDocumentClass->selectPart = doc_selectPart;
1189  m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
1190  m_pDocumentClass->getPartName = doc_getPartName;
1191  m_pDocumentClass->setPartMode = doc_setPartMode;
1192  m_pDocumentClass->paintTile = doc_paintTile;
1193 #ifdef IOS
1194  m_pDocumentClass->paintTileToCGContext = doc_paintTileToCGContext;
1195 #endif
1196  m_pDocumentClass->paintPartTile = doc_paintPartTile;
1197  m_pDocumentClass->getTileMode = doc_getTileMode;
1198  m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
1199  m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
1200  m_pDocumentClass->registerCallback = doc_registerCallback;
1201  m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
1202  m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent;
1203  m_pDocumentClass->removeTextContext = doc_removeTextContext;
1204  m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent;
1205  m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
1206  m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent;
1207  m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent;
1208  m_pDocumentClass->postUnoCommand = doc_postUnoCommand;
1209  m_pDocumentClass->setTextSelection = doc_setTextSelection;
1210  m_pDocumentClass->setWindowTextSelection = doc_setWindowTextSelection;
1211  m_pDocumentClass->getTextSelection = doc_getTextSelection;
1212  m_pDocumentClass->getSelectionType = doc_getSelectionType;
1213  m_pDocumentClass->getClipboard = doc_getClipboard;
1214  m_pDocumentClass->setClipboard = doc_setClipboard;
1215  m_pDocumentClass->paste = doc_paste;
1216  m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection;
1217  m_pDocumentClass->resetSelection = doc_resetSelection;
1218  m_pDocumentClass->getCommandValues = doc_getCommandValues;
1219  m_pDocumentClass->setClientZoom = doc_setClientZoom;
1220  m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea;
1221  m_pDocumentClass->setOutlineState = doc_setOutlineState;
1222 
1223  m_pDocumentClass->createView = doc_createView;
1224  m_pDocumentClass->destroyView = doc_destroyView;
1225  m_pDocumentClass->setView = doc_setView;
1226  m_pDocumentClass->getView = doc_getView;
1227  m_pDocumentClass->getViewsCount = doc_getViewsCount;
1228  m_pDocumentClass->getViewIds = doc_getViewIds;
1229 
1230  m_pDocumentClass->renderFont = doc_renderFont;
1231  m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
1232  m_pDocumentClass->getPartHash = doc_getPartHash;
1233 
1234  m_pDocumentClass->paintWindow = doc_paintWindow;
1235  m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI;
1236  m_pDocumentClass->paintWindowForView = doc_paintWindowForView;
1237  m_pDocumentClass->postWindow = doc_postWindow;
1238  m_pDocumentClass->resizeWindow = doc_resizeWindow;
1239 
1240  m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
1241 
1242  m_pDocumentClass->getPartInfo = doc_getPartInfo;
1243 
1244  m_pDocumentClass->insertCertificate = doc_insertCertificate;
1245  m_pDocumentClass->addCertificate = doc_addCertificate;
1246  m_pDocumentClass->getSignatureState = doc_getSignatureState;
1247 
1248  m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
1249  m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
1250 
1251  m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
1252  m_pDocumentClass->completeFunction = doc_completeFunction;
1253 
1255  }
1256  pClass = m_pDocumentClass.get();
1257 
1258 #ifndef IOS
1259  forceSetClipboardForCurrentView(this);
1260 #endif
1261 }
1262 
1264 {
1265  try
1266  {
1267  mxComponent->dispose();
1268  }
1269  catch (const css::lang::DisposedException& rException)
1270  {
1271  SAL_WARN("lok", "failed to dispose document:" << rException.Message);
1272  }
1273 }
1274 
1275 static OUString getGenerator()
1276 {
1277  OUString sGenerator(
1278  Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"));
1279  OUString os("$_OS");
1280  ::rtl::Bootstrap::expandMacros(os);
1281  return sGenerator.replaceFirst("%1", os);
1282 }
1283 
1284 extern "C" {
1285 
1286 CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
1287  : Idle( "lokit timer callback" ),
1288  m_pDocument(pDocument),
1289  m_pCallback(pCallback),
1290  m_pData(pData),
1291  m_nDisableCallbacks(0)
1292 {
1293  SetPriority(TaskPriority::POST_PAINT);
1294 
1295  // Add the states that are safe to skip duplicates on, even when
1296  // not consequent (i.e. do no emit them if unchanged from last).
1297  m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL");
1298  m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL");
1299  m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL");
1300  m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL");
1301  m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL");
1302  m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL");
1303  m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL");
1304  m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL");
1305  m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL");
1306  m_states.emplace(LOK_CALLBACK_SET_PART, "NIL");
1307 
1308  Start();
1309 }
1310 
1312 {
1313  Stop();
1314 }
1315 
1316 void CallbackFlushHandler::callback(const int type, const char* payload, void* data)
1317 {
1318  CallbackFlushHandler* self = static_cast<CallbackFlushHandler*>(data);
1319  if (self)
1320  {
1321  self->queue(type, payload);
1322  }
1323 }
1324 
1325 void CallbackFlushHandler::queue(const int type, const char* data)
1326 {
1327  comphelper::ProfileZone aZone("CallbackFlushHander::queue");
1328 
1329  CallbackData aCallbackData(type, (data ? data : "(nil)"));
1330  const std::string& payload = aCallbackData.PayloadString;
1331  SAL_INFO("lok", "Queue: [" << type << "]: [" << payload << "] on " << m_queue.size() << " entries.");
1332 
1333  bool bIsChartActive = false;
1334  if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
1335  {
1336  LokChartHelper aChartHelper(SfxViewShell::Current());
1337  bIsChartActive = aChartHelper.GetWindow() != nullptr;
1338  }
1339 
1340  if (callbacksDisabled() && !bIsChartActive)
1341  {
1342  // We drop notifications when this is set, except for important ones.
1343  // When we issue a complex command (such as .uno:InsertAnnotation)
1344  // there will be multiple notifications. On the first invalidation
1345  // we will start painting, but other events will get fired
1346  // while the complex command in question executes.
1347  // We don't want to suppress everything here on the wrong assumption
1348  // that no new events are fired during painting.
1349  if (type != LOK_CALLBACK_STATE_CHANGED &&
1350  type != LOK_CALLBACK_INVALIDATE_TILES &&
1351  type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1352  type != LOK_CALLBACK_CURSOR_VISIBLE &&
1353  type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
1354  type != LOK_CALLBACK_TEXT_SELECTION &&
1355  type != LOK_CALLBACK_REFERENCE_MARKS)
1356  {
1357  SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << payload << "].");
1358  return;
1359  }
1360 
1361  // In Writer we drop all notifications during painting.
1362  if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
1363  return;
1364  }
1365 
1366  // Suppress invalid payloads.
1367  if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1368  payload.find(", 0, 0, ") != std::string::npos)
1369  {
1370  // The cursor position is often the relative coordinates of the widget
1371  // issuing it, instead of the absolute one that we expect.
1372  // This is temporary however, and, once the control is created and initialized
1373  // correctly, it eventually emits the correct absolute coordinates.
1374  SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1375  return;
1376  }
1377 
1378  std::unique_lock<std::mutex> lock(m_mutex);
1379 
1380  // drop duplicate callbacks for the listed types
1381  switch (type)
1382  {
1383  case LOK_CALLBACK_TEXT_SELECTION_START:
1384  case LOK_CALLBACK_TEXT_SELECTION_END:
1385  case LOK_CALLBACK_TEXT_SELECTION:
1386  case LOK_CALLBACK_GRAPHIC_SELECTION:
1387  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1388  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1389  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1390  case LOK_CALLBACK_STATE_CHANGED:
1391  case LOK_CALLBACK_MOUSE_POINTER:
1392  case LOK_CALLBACK_CELL_CURSOR:
1393  case LOK_CALLBACK_CELL_VIEW_CURSOR:
1394  case LOK_CALLBACK_CELL_FORMULA:
1395  case LOK_CALLBACK_CELL_ADDRESS:
1396  case LOK_CALLBACK_CURSOR_VISIBLE:
1397  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1398  case LOK_CALLBACK_SET_PART:
1399  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1400  case LOK_CALLBACK_INVALIDATE_HEADER:
1401  case LOK_CALLBACK_WINDOW:
1402  case LOK_CALLBACK_CALC_FUNCTION_LIST:
1403  {
1404  const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
1405  [type] (const queue_type::value_type& elem) { return (elem.Type == type); });
1406 
1407  if (pos != m_queue.rend() && pos->PayloadString == payload)
1408  {
1409  SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << payload << "].");
1410  return;
1411  }
1412  }
1413  break;
1414  }
1415 
1416  if (type == LOK_CALLBACK_TEXT_SELECTION && payload.empty())
1417  {
1418  const auto& posStart = std::find_if(m_queue.rbegin(), m_queue.rend(),
1419  [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_START); });
1420  if (posStart != m_queue.rend())
1421  posStart->PayloadString.clear();
1422 
1423  const auto& posEnd = std::find_if(m_queue.rbegin(), m_queue.rend(),
1424  [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_END); });
1425  if (posEnd != m_queue.rend())
1426  posEnd->PayloadString.clear();
1427  }
1428 
1429  // When payload is empty discards any previous state.
1430  if (payload.empty())
1431  {
1432  switch (type)
1433  {
1434  case LOK_CALLBACK_TEXT_SELECTION_START:
1435  case LOK_CALLBACK_TEXT_SELECTION_END:
1436  case LOK_CALLBACK_TEXT_SELECTION:
1437  case LOK_CALLBACK_GRAPHIC_SELECTION:
1438  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1439  case LOK_CALLBACK_INVALIDATE_TILES:
1440  if (removeAll(
1441  [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1442  SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1443  break;
1444  }
1445  }
1446  else
1447  {
1448  switch (type)
1449  {
1450  // These are safe to use the latest state and ignore previous
1451  // ones (if any) since the last overrides previous ones.
1452  case LOK_CALLBACK_TEXT_SELECTION_START:
1453  case LOK_CALLBACK_TEXT_SELECTION_END:
1454  case LOK_CALLBACK_TEXT_SELECTION:
1455  case LOK_CALLBACK_MOUSE_POINTER:
1456  case LOK_CALLBACK_CELL_CURSOR:
1457  case LOK_CALLBACK_CELL_FORMULA:
1458  case LOK_CALLBACK_CELL_ADDRESS:
1459  case LOK_CALLBACK_CURSOR_VISIBLE:
1460  case LOK_CALLBACK_SET_PART:
1461  case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1462  case LOK_CALLBACK_RULER_UPDATE:
1463  {
1464  if (removeAll(
1465  [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1466  SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1467  }
1468  break;
1469 
1470  // These are safe to use the latest state and ignore previous
1471  // ones (if any) since the last overrides previous ones,
1472  // but only if the view is the same.
1473  case LOK_CALLBACK_CELL_VIEW_CURSOR:
1474  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1475  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1476  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1477  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1478  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1479  case LOK_CALLBACK_CALC_FUNCTION_LIST:
1480  {
1481  const int nViewId = lcl_getViewId(payload);
1482  removeAll(
1483  [type, nViewId] (const queue_type::value_type& elem) {
1484  return (elem.Type == type && nViewId == lcl_getViewId(elem));
1485  }
1486  );
1487  }
1488  break;
1489 
1490  case LOK_CALLBACK_INVALIDATE_TILES:
1491  if (processInvalidateTilesEvent(aCallbackData))
1492  return;
1493  break;
1494 
1495  // State changes with same name override previous ones with a different value.
1496  // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
1497  case LOK_CALLBACK_STATE_CHANGED:
1498  {
1499  // Compare the state name=value and overwrite earlier entries with same name.
1500  const auto pos = payload.find('=');
1501  if (pos != std::string::npos)
1502  {
1503  const std::string name = payload.substr(0, pos + 1);
1504  removeAll(
1505  [type, &name] (const queue_type::value_type& elem) {
1506  return (elem.Type == type) && (elem.PayloadString.compare(0, name.size(), name) == 0);
1507  }
1508  );
1509  }
1510  }
1511  break;
1512 
1513  case LOK_CALLBACK_WINDOW:
1514  if (processWindowEvent(aCallbackData))
1515  return;
1516  break;
1517 
1518  case LOK_CALLBACK_GRAPHIC_SELECTION:
1519  {
1520  // remove only selection ranges and 'EMPTY' messages
1521  // always send 'INPLACE' and 'INPLACE EXIT' messages
1522  removeAll([type, payload] (const queue_type::value_type& elem)
1523  { return (elem.Type == type && elem.PayloadString[0] != 'I'); });
1524  }
1525  break;
1526  }
1527  }
1528 
1529  // Validate that the cached data and the payload string are identical.
1530  assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
1531  m_queue.emplace_back(aCallbackData);
1532  SAL_INFO("lok", "Queued #" << (m_queue.size() - 1) <<
1533  " [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
1534 
1535 #ifdef DBG_UTIL
1536  {
1537  // Dump the queue state and validate cached data.
1538  int i = 1;
1539  std::ostringstream oss;
1540  if (m_queue.empty())
1541  oss << "Empty";
1542  else
1543  oss << m_queue.size() << " items\n";
1544  for (const CallbackData& c : m_queue)
1545  oss << i++ << ": [" << c.Type << "] [" << c.PayloadString << "].\n";
1546  SAL_INFO("lok", "Current Queue: " << oss.str());
1547  for (const CallbackData& c : m_queue)
1548  assert(c.validate());
1549  }
1550 #endif
1551 
1552  lock.unlock();
1553  if (!IsActive())
1554  {
1555  Start();
1556  }
1557 }
1558 
1560 {
1561  const std::string& payload = aCallbackData.PayloadString;
1562  const int type = aCallbackData.Type;
1563 
1564  RectangleAndPart& rcNew = aCallbackData.setRectangleAndPart(payload);
1565  if (rcNew.isEmpty())
1566  {
1567  SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1568  return true;
1569  }
1570 
1571  // If we have to invalidate all tiles, we can skip any new tile invalidation.
1572  // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
1573  const auto& pos
1574  = std::find_if(m_queue.rbegin(), m_queue.rend(), [](const queue_type::value_type& elem) {
1575  return (elem.Type == LOK_CALLBACK_INVALIDATE_TILES);
1576  });
1577  if (pos != m_queue.rend())
1578  {
1579  const RectangleAndPart& rcOld = pos->getRectangleAndPart();
1580  if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
1581  {
1582  SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1583  << "] since all tiles need to be invalidated.");
1584  return true;
1585  }
1586 
1587  if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
1588  {
1589  // If fully overlapping.
1590  if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1591  {
1592  SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1593  << "] since overlaps existing all-parts.");
1594  return true;
1595  }
1596  }
1597  }
1598 
1599  if (rcNew.isInfinite())
1600  {
1601  SAL_INFO("lok", "Have Empty [" << type << "]: [" << payload
1602  << "] so removing all with part " << rcNew.m_nPart << ".");
1603  removeAll([&rcNew](const queue_type::value_type& elem) {
1604  if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1605  {
1606  // Remove exiting if new is all-encompassing, or if of the same part.
1607  const RectangleAndPart rcOld = RectangleAndPart::Create(elem.PayloadString);
1608  return (rcNew.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart);
1609  }
1610 
1611  // Keep others.
1612  return false;
1613  });
1614  }
1615  else
1616  {
1617  const auto rcOrig = rcNew;
1618 
1619  SAL_INFO("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
1620  removeAll([&rcNew](const queue_type::value_type& elem) {
1621  if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1622  {
1623  const RectangleAndPart& rcOld = elem.getRectangleAndPart();
1624  if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
1625  {
1626  SAL_INFO("lok", "Nothing to merge between new: "
1627  << rcNew.toString() << ", and old: " << rcOld.toString());
1628  return false;
1629  }
1630 
1631  if (rcNew.m_nPart == -1)
1632  {
1633  // Don't merge unless fully overlapped.
1634  SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
1635  << "?");
1636  if (rcNew.m_aRectangle.IsInside(rcOld.m_aRectangle))
1637  {
1638  SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1639  << rcOld.toString() << ".");
1640  return true;
1641  }
1642  }
1643  else if (rcOld.m_nPart == -1)
1644  {
1645  // Don't merge unless fully overlapped.
1646  SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
1647  << "?");
1648  if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1649  {
1650  SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1651  << rcOld.toString() << ".");
1652  return true;
1653  }
1654  }
1655  else
1656  {
1657  const tools::Rectangle rcOverlap
1658  = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
1659  const bool bOverlap = !rcOverlap.IsEmpty();
1660  SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
1661  << " => " << rcOverlap.toString()
1662  << " Overlap: " << bOverlap);
1663  if (bOverlap)
1664  {
1665  rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
1666  SAL_INFO("lok", "Merged: " << rcNew.toString());
1667  return true;
1668  }
1669  }
1670  }
1671 
1672  // Keep others.
1673  return false;
1674  });
1675 
1676  if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
1677  {
1678  SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
1679  if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
1680  || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
1681  {
1682  SAL_WARN("lok", "Error: merged rect smaller.");
1683  }
1684  }
1685  }
1686 
1687  aCallbackData.setRectangleAndPart(rcNew);
1688  // Queue this one.
1689  return false;
1690 }
1691 
1692 bool CallbackFlushHandler::processWindowEvent(CallbackData& aCallbackData)
1693 {
1694  const std::string& payload = aCallbackData.PayloadString;
1695  const int type = aCallbackData.Type;
1696 
1697  boost::property_tree::ptree& aTree = aCallbackData.setJson(payload);
1698  const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
1699  const std::string aAction = aTree.get<std::string>("action", "");
1700  if (aAction == "invalidate")
1701  {
1702  std::string aRectStr = aTree.get<std::string>("rectangle", "");
1703  // no 'rectangle' field => invalidate all of the window =>
1704  // remove all previous window part invalidations
1705  if (aRectStr.empty())
1706  {
1707  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1708  if (elem.Type == LOK_CALLBACK_WINDOW)
1709  {
1710  const boost::property_tree::ptree& aOldTree = elem.getJson();
1711  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1712  && aOldTree.get<std::string>("action", "") == "invalidate")
1713  {
1714  return true;
1715  }
1716  }
1717  return false;
1718  });
1719  }
1720  else
1721  {
1722  // if we have to invalidate all of the window, ignore
1723  // any part invalidation message
1724  const auto invAllExist = std::any_of(m_queue.rbegin(), m_queue.rend(),
1725  [&nLOKWindowId] (const queue_type::value_type& elem)
1726  {
1727  if (elem.Type != LOK_CALLBACK_WINDOW)
1728  return false;
1729 
1730  const boost::property_tree::ptree& aOldTree = elem.getJson();
1731  return nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1732  && aOldTree.get<std::string>("action", "") == "invalidate"
1733  && aOldTree.get<std::string>("rectangle", "").empty();
1734  });
1735 
1736  // we found a invalidate-all window callback
1737  if (invAllExist)
1738  {
1739  SAL_INFO("lok.dialog", "Skipping queue ["
1740  << type << "]: [" << payload
1741  << "] since whole window needs to be invalidated.");
1742  return true;
1743  }
1744 
1745  std::istringstream aRectStream(aRectStr);
1746  long nLeft, nTop, nWidth, nHeight;
1747  char nComma;
1748  aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
1749  tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
1750  bool currentIsRedundant = false;
1751  removeAll([&aNewRect, &nLOKWindowId,
1752  &currentIsRedundant](const queue_type::value_type& elem) {
1753  if (elem.Type != LOK_CALLBACK_WINDOW)
1754  return false;
1755 
1756  const boost::property_tree::ptree& aOldTree = elem.getJson();
1757  if (aOldTree.get<std::string>("action", "") == "invalidate")
1758  {
1759  // Not possible that we encounter an empty rectangle here; we already handled this case above.
1760  std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
1761  long nOldLeft, nOldTop, nOldWidth, nOldHeight;
1762  char nOldComma;
1763  aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
1764  >> nOldComma >> nOldHeight;
1765  const tools::Rectangle aOldRect = tools::Rectangle(
1766  nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
1767 
1768  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1769  {
1770  if (aNewRect == aOldRect)
1771  {
1772  SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
1773  << "]. Skipping new.");
1774  // We have a rectangle in the queue already that makes the current Callback useless.
1775  currentIsRedundant = true;
1776  return false;
1777  }
1778  // new one engulfs the old one?
1779  else if (aNewRect.IsInside(aOldRect))
1780  {
1781  SAL_INFO("lok.dialog",
1782  "New rect [" << aNewRect.toString() << "] engulfs old ["
1783  << aOldRect.toString() << "]. Replacing old.");
1784  return true;
1785  }
1786  // old one engulfs the new one?
1787  else if (aOldRect.IsInside(aNewRect))
1788  {
1789  SAL_INFO("lok.dialog",
1790  "Old rect [" << aOldRect.toString() << "] engulfs new ["
1791  << aNewRect.toString() << "]. Skipping new.");
1792  // We have a rectangle in the queue already that makes the current Callback useless.
1793  currentIsRedundant = true;
1794  return false;
1795  }
1796  else
1797  {
1798  // Overlapping rects.
1799  const tools::Rectangle aPreMergeRect = aNewRect;
1800  aNewRect.Union(aOldRect);
1801  SAL_INFO("lok.dialog", "Merging rects ["
1802  << aPreMergeRect.toString() << "] & ["
1803  << aOldRect.toString() << "] = ["
1804  << aNewRect.toString()
1805  << "]. Replacing old.");
1806  return true;
1807  }
1808  }
1809  }
1810 
1811  // keep rest
1812  return false;
1813  });
1814 
1815  // Do not enqueue if redundant.
1816  if (currentIsRedundant)
1817  return true;
1818 
1819  aTree.put("rectangle", aNewRect.toString().getStr());
1820  aCallbackData.setJson(aTree);
1821  assert(aCallbackData.validate() && "Validation after setJson failed!");
1822  }
1823  }
1824  else if (aAction == "created")
1825  {
1826  // Remove all previous actions on same dialog, if we are creating it anew.
1827  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1828  if (elem.Type == LOK_CALLBACK_WINDOW)
1829  {
1830  const boost::property_tree::ptree& aOldTree = elem.getJson();
1831  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1832  return true;
1833  }
1834  return false;
1835  });
1836 
1837  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
1838  if (!pWindow)
1839  {
1840  gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
1841  return false;
1842  }
1843 
1844 #ifndef IOS
1845  auto xClip = forceSetClipboardForCurrentView(m_pDocument);
1846 
1847  uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip.get());
1848  pWindow->SetClipboard(xClipboard);
1849 #endif
1850  }
1851  else if (aAction == "size_changed")
1852  {
1853  // A size change is practically re-creation of the window.
1854  // But at a minimum it's a full invalidation.
1855  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1856  if (elem.Type == LOK_CALLBACK_WINDOW)
1857  {
1858  const boost::property_tree::ptree& aOldTree = elem.getJson();
1859  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1860  {
1861  const std::string aOldAction = aOldTree.get<std::string>("action", "");
1862  if (aOldAction == "invalidate")
1863  return true;
1864  }
1865  }
1866  return false;
1867  });
1868  }
1869 
1870  // Queue this one.
1871  return false;
1872 }
1873 
1874 void CallbackFlushHandler::Invoke()
1875 {
1876  comphelper::ProfileZone aZone("CallbackFlushHander::Invoke");
1877 
1878  if (m_pCallback)
1879  {
1880  std::scoped_lock<std::mutex> lock(m_mutex);
1881 
1882  SAL_INFO("lok", "Flushing " << m_queue.size() << " elements.");
1883  for (const auto& rCallbackData : m_queue)
1884  {
1885  const int type = rCallbackData.Type;
1886  const auto& payload = rCallbackData.PayloadString;
1887  const int viewId = lcl_isViewCallbackType(type) ? lcl_getViewId(rCallbackData) : -1;
1888 
1889  if (viewId == -1)
1890  {
1891  const auto stateIt = m_states.find(type);
1892  if (stateIt != m_states.end())
1893  {
1894  // If the state didn't change, it's safe to ignore.
1895  if (stateIt->second == payload)
1896  {
1897  SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
1898  continue;
1899  }
1900 
1901  stateIt->second = payload;
1902  }
1903  }
1904  else
1905  {
1906  const auto statesIt = m_viewStates.find(viewId);
1907  if (statesIt != m_viewStates.end())
1908  {
1909  auto& states = statesIt->second;
1910  const auto stateIt = states.find(type);
1911  if (stateIt != states.end())
1912  {
1913  // If the state didn't change, it's safe to ignore.
1914  if (stateIt->second == payload)
1915  {
1916  SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
1917  continue;
1918  }
1919 
1920  SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
1921  stateIt->second = payload;
1922  }
1923  else
1924  {
1925  SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
1926  states.emplace(type, payload);
1927 
1928  }
1929  }
1930  }
1931 
1932  m_pCallback(type, payload.c_str(), m_pData);
1933  }
1934 
1935  m_queue.clear();
1936  }
1937 }
1938 
1939 bool CallbackFlushHandler::removeAll(const std::function<bool (const CallbackFlushHandler::queue_type::value_type&)>& rTestFunc)
1940 {
1941  auto newEnd = std::remove_if(m_queue.begin(), m_queue.end(), rTestFunc);
1942  if (newEnd != m_queue.end())
1943  {
1944  m_queue.erase(newEnd, m_queue.end());
1945  return true;
1946  }
1947 
1948  return false;
1949 }
1950 
1952 {
1953  const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
1954  if (!result.second && result.first != m_viewStates.end())
1955  {
1956  result.first->second.clear();
1957  }
1958 }
1959 
1961 {
1962  m_viewStates.erase(viewId);
1963 }
1964 
1965 
1966 static void doc_destroy(LibreOfficeKitDocument *pThis)
1967 {
1968  comphelper::ProfileZone aZone("doc_destroy");
1969 
1970  SolarMutexGuard aGuard;
1971 
1973 
1974  LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
1975  delete pDocument;
1976 }
1977 
1978 static void lo_destroy (LibreOfficeKit* pThis);
1979 static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
1980 static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL);
1981 static char * lo_getError (LibreOfficeKit* pThis);
1982 static void lo_freeError (char* pFree);
1983 static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis,
1984  const char* pURL,
1985  const char* pOptions);
1986 static void lo_registerCallback (LibreOfficeKit* pThis,
1987  LibreOfficeKitCallback pCallback,
1988  void* pData);
1989 static char* lo_getFilterTypes(LibreOfficeKit* pThis);
1990 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
1991 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
1992  const char* pURL,
1993  const char* pPassword);
1994 static char* lo_getVersionInfo(LibreOfficeKit* pThis);
1995 static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
1996 
1997 static bool lo_signDocument(LibreOfficeKit* pThis,
1998  const char* pUrl,
1999  const unsigned char* pCertificateBinary,
2000  const int nCertificateBinarySize,
2001  const unsigned char* pPrivateKeyBinary,
2002  const int nPrivateKeyBinarySize);
2003 
2004 static void lo_runLoop(LibreOfficeKit* pThis,
2005  LibreOfficeKitPollCallback pPollCallback,
2006  LibreOfficeKitWakeCallback pWakeCallback,
2007  void* pData);
2008 
2010  : m_pOfficeClass( gOfficeClass.lock() )
2011  , maThread(nullptr)
2012  , mpCallback(nullptr)
2013  , mpCallbackData(nullptr)
2014  , mOptionalFeatures(0)
2015 {
2016  if(!m_pOfficeClass) {
2017  m_pOfficeClass = std::make_shared<LibreOfficeKitClass>();
2018  m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
2019 
2020  m_pOfficeClass->destroy = lo_destroy;
2021  m_pOfficeClass->documentLoad = lo_documentLoad;
2022  m_pOfficeClass->getError = lo_getError;
2023  m_pOfficeClass->freeError = lo_freeError;
2024  m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
2025  m_pOfficeClass->registerCallback = lo_registerCallback;
2026  m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
2027  m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
2028  m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
2029  m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
2030  m_pOfficeClass->runMacro = lo_runMacro;
2031  m_pOfficeClass->signDocument = lo_signDocument;
2032  m_pOfficeClass->runLoop = lo_runLoop;
2033 
2035  }
2036 
2037  pClass = m_pOfficeClass.get();
2038 }
2039 
2041 {
2042 }
2043 
2044 namespace
2045 {
2046 
2047 #ifdef IOS
2048 void paintTileToCGContext(ITiledRenderable* pDocument,
2049  void* rCGContext, const Size nCanvasSize,
2050  const int nTilePosX, const int nTilePosY,
2051  const int nTileWidth, const int nTileHeight)
2052 {
2054  aData.rCGContext = reinterpret_cast<CGContextRef>(rCGContext);
2055 
2056  ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
2057  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2058  pDevice->SetOutputSizePixel(nCanvasSize);
2059  pDocument->paintTile(*pDevice, nCanvasSize.Width(), nCanvasSize.Height(),
2060  nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2061 }
2062 
2063 void paintTileIOS(LibreOfficeKitDocument* pThis,
2064  unsigned char* pBuffer,
2065  const int nCanvasWidth, const int nCanvasHeight, const double fDPIScale,
2066  const int nTilePosX, const int nTilePosY,
2067  const int nTileWidth, const int nTileHeight)
2068 {
2069  CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
2070  nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
2071  kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
2072 
2073  CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
2074  CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
2075 
2076  doc_paintTileToCGContext(pThis, (void*) pCGContext, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2077 
2078  CGContextRelease(pCGContext);
2079 }
2080 #endif
2081 
2082 void setLanguageAndLocale(OUString const & aLangISO)
2083 {
2084  SvtSysLocaleOptions aLocalOptions;
2085  aLocalOptions.SetLocaleConfigString(aLangISO);
2086  aLocalOptions.SetUILocaleConfigString(aLangISO);
2087  aLocalOptions.Commit();
2088 }
2089 
2090 } // anonymous namespace
2091 
2092 // Wonder global state ...
2093 static uno::Reference<css::uno::XComponentContext> xContext;
2094 static uno::Reference<css::lang::XMultiServiceFactory> xSFactory;
2095 static uno::Reference<css::lang::XMultiComponentFactory> xFactory;
2096 
2097 static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
2098 {
2099  return lo_documentLoadWithOptions(pThis, pURL, nullptr);
2100 }
2101 
2102 static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
2103 {
2104  comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
2105 
2106  SolarMutexGuard aGuard;
2107 
2108  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2109  pLib->maLastExceptionMsg.clear();
2110 
2111  OUString aURL(getAbsoluteURL(pURL));
2112  if (aURL.isEmpty())
2113  {
2114  pLib->maLastExceptionMsg = "Filename to load was not provided.";
2115  SAL_INFO("lok", "URL for load is empty");
2116  return nullptr;
2117  }
2118 
2119  pLib->maLastExceptionMsg.clear();
2120 
2121  if (!xContext.is())
2122  {
2123  pLib->maLastExceptionMsg = "ComponentContext is not available";
2124  SAL_INFO("lok", "ComponentContext is not available");
2125  return nullptr;
2126  }
2127 
2128  uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2129 
2130  if (!xComponentLoader.is())
2131  {
2132  pLib->maLastExceptionMsg = "ComponentLoader is not available";
2133  SAL_INFO("lok", "ComponentLoader is not available");
2134  return nullptr;
2135  }
2136 
2137  try
2138  {
2139  // 'Language=...' is an option that LOK consumes by itself, and does
2140  // not pass it as a parameter to the filter
2141  OUString aOptions = getUString(pOptions);
2142  const OUString aLanguage = extractParameter(aOptions, "Language");
2143 
2144  if (!aLanguage.isEmpty())
2145  {
2147  // Set the LOK language tag, used for dialog tunneling.
2150 
2151  SAL_INFO("lok", "Set document language to " << aLanguage);
2152  // use with care - it sets it for the entire core, not just the
2153  // document
2154  setLanguageAndLocale(aLanguage);
2155  // Need to reset the static initialized values
2157  }
2158 
2159  uno::Sequence<css::beans::PropertyValue> aFilterOptions(2);
2160  aFilterOptions[0] = css::beans::PropertyValue( "FilterOptions",
2161  0,
2162  uno::makeAny(aOptions),
2163  beans::PropertyState_DIRECT_VALUE);
2164 
2165  rtl::Reference<LOKInteractionHandler> const pInteraction(
2166  new LOKInteractionHandler("load", pLib));
2167  auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
2168  comphelper::ScopeGuard const g([&] () {
2169  if (pair.second)
2170  {
2171  pLib->mInteractionMap.erase(aURL.toUtf8());
2172  }
2173  });
2174  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
2175  aFilterOptions[1].Name = "InteractionHandler";
2176  aFilterOptions[1].Value <<= xInteraction;
2177 
2178  /* TODO
2179  sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
2180  aFilterOptions[2].Name = "MacroExecutionMode";
2181  aFilterOptions[2].Value <<= nMacroExecMode;
2182 
2183  sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
2184  aFilterOptions[3].Name = "UpdateDocMode";
2185  aFilterOptions[3].Value <<= nUpdateDoc;
2186  */
2187 
2188  uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
2189  aURL, "_blank", 0,
2190  aFilterOptions);
2191 
2192  assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
2193 
2194  if (!xComponent.is())
2195  {
2196  pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
2197  SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
2198  return nullptr;
2199  }
2200 
2201  LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent);
2202  if (pLib->mpCallback)
2203  {
2204  int nState = doc_getSignatureState(pDocument);
2205  pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
2206  }
2207  return pDocument;
2208  }
2209  catch (const uno::Exception& exception)
2210  {
2211  pLib->maLastExceptionMsg = exception.Message;
2212  TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
2213  }
2214 
2215  return nullptr;
2216 }
2217 
2218 static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
2219 {
2220  comphelper::ProfileZone aZone("lo_runMacro");
2221 
2222  SolarMutexGuard aGuard;
2223 
2224  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2225  pLib->maLastExceptionMsg.clear();
2226 
2227  OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
2228  if (sURL.isEmpty())
2229  {
2230  pLib->maLastExceptionMsg = "Macro to run was not provided.";
2231  SAL_INFO("lok", "Macro URL is empty");
2232  return false;
2233  }
2234 
2235  if (!sURL.startsWith("macro://"))
2236  {
2237  pLib->maLastExceptionMsg = "This doesn't look like macro URL";
2238  SAL_INFO("lok", "Macro URL is invalid");
2239  return false;
2240  }
2241 
2242  pLib->maLastExceptionMsg.clear();
2243 
2244  if (!xContext.is())
2245  {
2246  pLib->maLastExceptionMsg = "ComponentContext is not available";
2247  SAL_INFO("lok", "ComponentContext is not available");
2248  return false;
2249  }
2250 
2251  util::URL aURL;
2252  aURL.Complete = sURL;
2253 
2254  uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) );
2255 
2256  if( xParser.is() )
2257  xParser->parseStrict( aURL );
2258 
2259  uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2260 
2261  if (!xComponentLoader.is())
2262  {
2263  pLib->maLastExceptionMsg = "ComponentLoader is not available";
2264  SAL_INFO("lok", "ComponentLoader is not available");
2265  return false;
2266  }
2267 
2268  xFactory = xContext->getServiceManager();
2269 
2270  if (xFactory.is())
2271  {
2272  uno::Reference<frame::XDispatchProvider> xDP;
2273  xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
2274  xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
2275  uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
2276 
2277  if (!xD.is())
2278  {
2279  pLib->maLastExceptionMsg = "Macro loader is not available";
2280  SAL_INFO("lok", "Macro loader is not available");
2281  return false;
2282  }
2283 
2284  uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
2285  uno::Sequence<css::beans::PropertyValue> aEmpty;
2286  css::beans::PropertyValue aErr;
2287  uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
2288  aRet >>= aErr;
2289 
2290  if (aErr.Name == "ErrorCode")
2291  {
2292  sal_uInt32 nErrCode = 0; // ERRCODE_NONE
2293  aErr.Value >>= nErrCode;
2294 
2295  pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
2296  SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
2297 
2298  return false;
2299  }
2300 
2301  return true;
2302  }
2303 
2304  return false;
2305 }
2306 
2307 static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
2308  const char* pURL,
2309  const unsigned char* pCertificateBinary,
2310  const int nCertificateBinarySize,
2311  const unsigned char* pPrivateKeyBinary,
2312  const int nPrivateKeyBinarySize)
2313 {
2314  comphelper::ProfileZone aZone("lo_signDocument");
2315 
2316  OUString aURL(getAbsoluteURL(pURL));
2317  if (aURL.isEmpty())
2318  return false;
2319 
2320  if (!xContext.is())
2321  return false;
2322 
2323  uno::Sequence<sal_Int8> aCertificateSequence;
2324 
2325  std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
2326  std::string aCertificateBase64String = extractCertificate(aCertificateString);
2327  if (!aCertificateBase64String.empty())
2328  {
2329  OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
2330  comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
2331  }
2332  else
2333  {
2334  aCertificateSequence.realloc(nCertificateBinarySize);
2335  std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
2336  }
2337 
2338  uno::Sequence<sal_Int8> aPrivateKeySequence;
2339  std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
2340  std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
2341  if (!aPrivateKeyBase64String.empty())
2342  {
2343  OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
2344  comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
2345  }
2346  else
2347  {
2348  aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
2349  std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.begin());
2350  }
2351 
2352  uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
2353  uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
2354  if (!xSecurityContext.is())
2355  return false;
2356 
2357  uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
2358  uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
2359 
2360  if (!xCertificateCreator.is())
2361  return false;
2362 
2363  uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
2364 
2365  if (!xCertificate.is())
2366  return false;
2367 
2368  sfx2::DocumentSigner aDocumentSigner(aURL);
2369  if (!aDocumentSigner.signDocument(xCertificate))
2370  return false;
2371 
2372  return true;
2373 }
2374 
2375 static void lo_registerCallback (LibreOfficeKit* pThis,
2376  LibreOfficeKitCallback pCallback,
2377  void* pData)
2378 {
2379  SolarMutexGuard aGuard;
2380 
2381  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2382  pLib->maLastExceptionMsg.clear();
2383 
2384  pLib->mpCallback = pCallback;
2385  pLib->mpCallbackData = pData;
2386 }
2387 
2388 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
2389 {
2390  comphelper::ProfileZone aZone("doc_saveAs");
2391 
2392  SolarMutexGuard aGuard;
2394 
2395  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2396 
2397  OUString sFormat = getUString(pFormat);
2398  OUString aURL(getAbsoluteURL(sUrl));
2399  if (aURL.isEmpty())
2400  {
2401  SetLastExceptionMsg("Filename to save to was not provided.");
2402  SAL_INFO("lok", "URL for save is empty");
2403  return false;
2404  }
2405 
2406  try
2407  {
2408  const ExtensionMap* pMap;
2409 
2410  switch (doc_getDocumentType(pThis))
2411  {
2412  case LOK_DOCTYPE_SPREADSHEET:
2413  pMap = aCalcExtensionMap;
2414  break;
2415  case LOK_DOCTYPE_PRESENTATION:
2416  pMap = aImpressExtensionMap;
2417  break;
2418  case LOK_DOCTYPE_DRAWING:
2419  pMap = aDrawExtensionMap;
2420  break;
2421  case LOK_DOCTYPE_TEXT:
2422  pMap = aWriterExtensionMap;
2423  break;
2424  case LOK_DOCTYPE_OTHER:
2425  default:
2426  SAL_INFO("lok", "Can't save document - unsupported document type.");
2427  return false;
2428  }
2429 
2430  if (pFormat == nullptr)
2431  {
2432  // sniff from the extension
2433  sal_Int32 idx = aURL.lastIndexOf(".");
2434  if( idx > 0 )
2435  {
2436  sFormat = aURL.copy( idx + 1 );
2437  }
2438  else
2439  {
2440  SetLastExceptionMsg("input filename without a suffix");
2441  return false;
2442  }
2443  }
2444 
2445  OUString aFilterName;
2446  for (sal_Int32 i = 0; pMap[i].extn; ++i)
2447  {
2448  if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
2449  {
2450  aFilterName = getUString(pMap[i].filterName);
2451  break;
2452  }
2453  }
2454  if (aFilterName.isEmpty())
2455  {
2456  SetLastExceptionMsg("no output filter found for provided suffix");
2457  return false;
2458  }
2459 
2460  OUString aFilterOptions = getUString(pFilterOptions);
2461 
2462  // Check if watermark for pdf is passed by filteroptions...
2463  // It is not a real filter option so it must be filtered out.
2464  OUString watermarkText, sFullSheetPreview;
2465  int aIndex = -1;
2466  if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
2467  {
2468  int bIndex = aFilterOptions.indexOf("WATERMARKEND");
2469  watermarkText = aFilterOptions.copy(aIndex+11, bIndex-(aIndex+11));
2470 
2471  OUString temp = aFilterOptions.copy(0, aIndex);
2472  aFilterOptions = temp + aFilterOptions.copy(bIndex+12);
2473  }
2474 
2475  if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
2476  {
2477  int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
2478  sFullSheetPreview = aFilterOptions.copy(aIndex+18, bIndex-(aIndex+18));
2479 
2480  OUString temp = aFilterOptions.copy(0, aIndex);
2481  aFilterOptions = temp + aFilterOptions.copy(bIndex+16);
2482  }
2483 
2484  bool bFullSheetPreview = sFullSheetPreview == "true";
2485 
2486  // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
2487  // gets a new name). When this is not provided, the meaning of
2488  // saveAs() is more like save-a-copy, which allows saving to any
2489  // random format like PDF or PNG.
2490  // It is not a real filter option, so we have to filter it out.
2491  const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
2492  std::vector<OUString> aFilteredOptionVec;
2493  bool bTakeOwnership = false;
2494  MediaDescriptor aSaveMediaDescriptor;
2495  for (const auto& rOption : aOptionSeq)
2496  {
2497  if (rOption == "TakeOwnership")
2498  bTakeOwnership = true;
2499  else if (rOption == "NoFileSync")
2500  aSaveMediaDescriptor["NoFileSync"] <<= true;
2501  else
2502  aFilteredOptionVec.push_back(rOption);
2503  }
2504 
2505  aSaveMediaDescriptor["Overwrite"] <<= true;
2506  aSaveMediaDescriptor["FilterName"] <<= aFilterName;
2507 
2508  auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
2509  aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
2510  aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS()] <<= aFilterOptions;
2511 
2512  if(!watermarkText.isEmpty() || bFullSheetPreview)
2513  {
2514  uno::Sequence< beans::PropertyValue > aFilterData( static_cast<int>(bFullSheetPreview) + static_cast<int>(!watermarkText.isEmpty()) );
2515 
2516  if (!watermarkText.isEmpty())
2517  {
2518  aFilterData[ 0 ].Name = "TiledWatermark";
2519  aFilterData[ 0 ].Value <<= watermarkText;
2520  }
2521 
2522  if (bFullSheetPreview)
2523  {
2524  int nOptIndex = static_cast<int>(!watermarkText.isEmpty());
2525 
2526  aFilterData[ nOptIndex ].Name = "SinglePageSheets";
2527  aFilterData[ nOptIndex ].Value <<= true;
2528  }
2529 
2530  aSaveMediaDescriptor["FilterData"] <<= aFilterData;
2531  }
2532 
2533  // add interaction handler too
2534  if (gImpl)
2535  {
2536  // gImpl does not have to exist when running from a unit test
2537  rtl::Reference<LOKInteractionHandler> const pInteraction(
2538  new LOKInteractionHandler("saveas", gImpl, pDocument));
2539  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
2540 
2541  aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER()] <<= xInteraction;
2542  }
2543 
2544  uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2545 
2546  if (bTakeOwnership)
2547  xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2548  else
2549  xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2550 
2551  return true;
2552  }
2553  catch (const uno::Exception& exception)
2554  {
2555  SetLastExceptionMsg("exception: " + exception.Message);
2556  }
2557  return false;
2558 }
2559 
2566 static void doc_iniUnoCommands ()
2567 {
2568  SolarMutexGuard aGuard;
2570 
2571  OUString sUnoCommands[] =
2572  {
2573  OUString(".uno:AlignLeft"),
2574  OUString(".uno:AlignHorizontalCenter"),
2575  OUString(".uno:AlignRight"),
2576  OUString(".uno:BackColor"),
2577  OUString(".uno:BackgroundColor"),
2578  OUString(".uno:TableCellBackgroundColor"),
2579  OUString(".uno:Bold"),
2580  OUString(".uno:CenterPara"),
2581  OUString(".uno:CharBackColor"),
2582  OUString(".uno:CharBackgroundExt"),
2583  OUString(".uno:CharFontName"),
2584  OUString(".uno:Color"),
2585  OUString(".uno:ControlCodes"),
2586  OUString(".uno:DecrementIndent"),
2587  OUString(".uno:DefaultBullet"),
2588  OUString(".uno:DefaultNumbering"),
2589  OUString(".uno:FontColor"),
2590  OUString(".uno:FontHeight"),
2591  OUString(".uno:IncrementIndent"),
2592  OUString(".uno:Italic"),
2593  OUString(".uno:JustifyPara"),
2594  OUString(".uno:OutlineFont"),
2595  OUString(".uno:LeftPara"),
2596  OUString(".uno:LanguageStatus"),
2597  OUString(".uno:RightPara"),
2598  OUString(".uno:Shadowed"),
2599  OUString(".uno:SubScript"),
2600  OUString(".uno:SuperScript"),
2601  OUString(".uno:Strikeout"),
2602  OUString(".uno:StyleApply"),
2603  OUString(".uno:Underline"),
2604  OUString(".uno:ModifiedStatus"),
2605  OUString(".uno:Undo"),
2606  OUString(".uno:Redo"),
2607  OUString(".uno:InsertPage"),
2608  OUString(".uno:DeletePage"),
2609  OUString(".uno:DuplicatePage"),
2610  OUString(".uno:Cut"),
2611  OUString(".uno:Copy"),
2612  OUString(".uno:Paste"),
2613  OUString(".uno:SelectAll"),
2614  OUString(".uno:InsertAnnotation"),
2615  OUString(".uno:DeleteAnnotation"),
2616  OUString(".uno:ReplyComment"),
2617  OUString(".uno:ResolveComment"),
2618  OUString(".uno:InsertRowsBefore"),
2619  OUString(".uno:InsertRowsAfter"),
2620  OUString(".uno:InsertColumnsBefore"),
2621  OUString(".uno:InsertColumnsAfter"),
2622  OUString(".uno:DeleteRows"),
2623  OUString(".uno:DeleteColumns"),
2624  OUString(".uno:DeleteTable"),
2625  OUString(".uno:SelectTable"),
2626  OUString(".uno:EntireRow"),
2627  OUString(".uno:EntireColumn"),
2628  OUString(".uno:EntireCell"),
2629  OUString(".uno:AssignLayout"),
2630  OUString(".uno:StatusDocPos"),
2631  OUString(".uno:RowColSelCount"),
2632  OUString(".uno:StatusPageStyle"),
2633  OUString(".uno:InsertMode"),
2634  OUString(".uno:SpellOnline"),
2635  OUString(".uno:StatusSelectionMode"),
2636  OUString(".uno:StateTableCell"),
2637  OUString(".uno:StatusBarFunc"),
2638  OUString(".uno:StatePageNumber"),
2639  OUString(".uno:StateWordCount"),
2640  OUString(".uno:SelectionMode"),
2641  OUString(".uno:PageStatus"),
2642  OUString(".uno:LayoutStatus"),
2643  OUString(".uno:Context"),
2644  OUString(".uno:WrapText"),
2645  OUString(".uno:ToggleMergeCells"),
2646  OUString(".uno:NumberFormatCurrency"),
2647  OUString(".uno:NumberFormatPercent"),
2648  OUString(".uno:NumberFormatDate"),
2649  OUString(".uno:FrameLineColor"),
2650  OUString(".uno:SortAscending"),
2651  OUString(".uno:SortDescending"),
2652  OUString(".uno:TrackChanges"),
2653  OUString(".uno:ShowTrackedChanges"),
2654  OUString(".uno:NextTrackedChange"),
2655  OUString(".uno:PreviousTrackedChange"),
2656  OUString(".uno:AcceptAllTrackedChanges"),
2657  OUString(".uno:RejectAllTrackedChanges"),
2658  OUString(".uno:TableDialog"),
2659  OUString(".uno:FormatCellDialog"),
2660  OUString(".uno:FontDialog"),
2661  OUString(".uno:ParagraphDialog"),
2662  OUString(".uno:OutlineBullet"),
2663  OUString(".uno:InsertIndexesEntry"),
2664  OUString(".uno:DocumentRepair"),
2665  OUString(".uno:TransformDialog"),
2666  OUString(".uno:InsertPageHeader"),
2667  OUString(".uno:InsertPageFooter"),
2668  OUString(".uno:OnlineAutoFormat"),
2669  OUString(".uno:InsertSymbol"),
2670  OUString(".uno:EditRegion"),
2671  OUString(".uno:ThesaurusDialog"),
2672  OUString(".uno:Orientation"),
2673  OUString(".uno:ObjectAlignLeft"),
2674  OUString(".uno:ObjectAlignRight"),
2675  OUString(".uno:AlignCenter")
2676  };
2677 
2678  util::URL aCommandURL;
2679  SfxViewShell* pViewShell = SfxViewShell::Current();
2680  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
2681 
2682  // check if Frame-Controller were created.
2683  if (!pViewFrame)
2684  {
2685  SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
2686  return;
2687  }
2688 
2689  if (!xContext.is())
2691  if (!xContext.is())
2692  {
2693  SAL_WARN("lok", "iniUnoCommands: Component context is not available");
2694  return;
2695  }
2696 
2697  SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
2698  uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
2699 
2700  for (const auto & sUnoCommand : sUnoCommands)
2701  {
2702  aCommandURL.Complete = sUnoCommand;
2703  xParser->parseStrict(aCommandURL);
2704 
2705  // when null, this command is not supported by the given component
2706  // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
2707  if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
2708  {
2709  // Initialize slot to dispatch .uno: Command.
2710  pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
2711  }
2712  }
2713 }
2714 
2715 static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
2716 {
2717  comphelper::ProfileZone aZone("doc_getDocumentType");
2718 
2719  SolarMutexGuard aGuard;
2721 
2722  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2723 
2724  try
2725  {
2726  uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2727 
2728  if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
2729  {
2730  return LOK_DOCTYPE_SPREADSHEET;
2731  }
2732  else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
2733  {
2734  return LOK_DOCTYPE_PRESENTATION;
2735  }
2736  else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
2737  {
2738  return LOK_DOCTYPE_DRAWING;
2739  }
2740  else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
2741  {
2742  return LOK_DOCTYPE_TEXT;
2743  }
2744  else
2745  {
2746  SetLastExceptionMsg("unknown document type");
2747  }
2748  }
2749  catch (const uno::Exception& exception)
2750  {
2751  SetLastExceptionMsg("exception: " + exception.Message);
2752  }
2753  return LOK_DOCTYPE_OTHER;
2754 }
2755 
2756 static int doc_getParts (LibreOfficeKitDocument* pThis)
2757 {
2758  comphelper::ProfileZone aZone("doc_getParts");
2759 
2760  SolarMutexGuard aGuard;
2761 
2762  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2763  if (!pDoc)
2764  {
2765  SetLastExceptionMsg("Document doesn't support tiled rendering");
2766  return 0;
2767  }
2768 
2769  return pDoc->getParts();
2770 }
2771 
2772 static int doc_getPart (LibreOfficeKitDocument* pThis)
2773 {
2774  comphelper::ProfileZone aZone("doc_getPart");
2775 
2776  SolarMutexGuard aGuard;
2778 
2779  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2780  if (!pDoc)
2781  {
2782  SetLastExceptionMsg("Document doesn't support tiled rendering");
2783  return 0;
2784  }
2785 
2786  return pDoc->getPart();
2787 }
2788 
2789 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
2790 {
2791  comphelper::ProfileZone aZone("doc_setPart");
2792 
2793  SolarMutexGuard aGuard;
2795 
2796  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2797  if (!pDoc)
2798  {
2799  SetLastExceptionMsg("Document doesn't support tiled rendering");
2800  return;
2801  }
2802 
2803  pDoc->setPart( nPart );
2804 }
2805 
2806 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
2807 {
2808  comphelper::ProfileZone aZone("doc_getPartInfo");
2809 
2810  SolarMutexGuard aGuard;
2811  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2812  if (!pDoc)
2813  {
2814  SetLastExceptionMsg("Document doesn't support tiled rendering");
2815  return nullptr;
2816  }
2817 
2818  return convertOUString(pDoc->getPartInfo(nPart));
2819 }
2820 
2821 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
2822 {
2823  SolarMutexGuard aGuard;
2824  if (gImpl)
2825  gImpl->maLastExceptionMsg.clear();
2826 
2827  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2828  if (!pDoc)
2829  {
2830  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
2831  return;
2832  }
2833 
2834  pDoc->selectPart( nPart, nSelect );
2835 }
2836 
2837 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
2838 {
2839  SolarMutexGuard aGuard;
2840  if (gImpl)
2841  gImpl->maLastExceptionMsg.clear();
2842 
2843  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2844  if (!pDoc)
2845  {
2846  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
2847  return;
2848  }
2849 
2850  pDoc->moveSelectedParts(nPosition, bDuplicate);
2851 }
2852 
2853 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
2854 {
2855  comphelper::ProfileZone aZone("doc_getPartPageRectangles");
2856 
2857  SolarMutexGuard aGuard;
2859 
2860  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2861  if (!pDoc)
2862  {
2863  SetLastExceptionMsg("Document doesn't support tiled rendering");
2864  return nullptr;
2865  }
2866 
2867  return convertOUString(pDoc->getPartPageRectangles());
2868 }
2869 
2870 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
2871 {
2872  comphelper::ProfileZone aZone("doc_getPartName");
2873 
2874  SolarMutexGuard aGuard;
2876 
2877  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2878  if (!pDoc)
2879  {
2880  SetLastExceptionMsg("Document doesn't support tiled rendering");
2881  return nullptr;
2882  }
2883 
2884  return convertOUString(pDoc->getPartName(nPart));
2885 }
2886 
2887 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
2888 {
2889  comphelper::ProfileZone aZone("doc_getPartHash");
2890 
2891  SolarMutexGuard aGuard;
2893 
2894  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2895  if (!pDoc)
2896  {
2897  SetLastExceptionMsg("Document doesn't support tiled rendering");
2898  return nullptr;
2899  }
2900 
2901  return convertOUString(pDoc->getPartHash(nPart));
2902 }
2903 
2904 static void doc_setPartMode(LibreOfficeKitDocument* pThis,
2905  int nPartMode)
2906 {
2907  comphelper::ProfileZone aZone("doc_setPartMode");
2908 
2909  SolarMutexGuard aGuard;
2911 
2912  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2913  if (!pDoc)
2914  {
2915  SetLastExceptionMsg("Document doesn't support tiled rendering");
2916  return;
2917  }
2918 
2919 
2920  int nCurrentPart = pDoc->getPart();
2921 
2922  pDoc->setPartMode(nPartMode);
2923 
2924  // We need to make sure the internal state is updated, just changing the mode
2925  // might not update the relevant shells (i.e. impress will keep rendering the
2926  // previous mode unless we do this).
2927  // TODO: we might want to do this within the relevant components rather than
2928  // here, but that's also dependent on how we implement embedded object
2929  // rendering I guess?
2930  // TODO: we could be clever and e.g. set to 0 when we change to/from
2931  // embedded object mode, and not when changing between slide/notes/combined
2932  // modes?
2933  if ( nCurrentPart < pDoc->getParts() )
2934  {
2935  pDoc->setPart( nCurrentPart );
2936  }
2937  else
2938  {
2939  pDoc->setPart( 0 );
2940  }
2941 }
2942 
2943 static void doc_paintTile(LibreOfficeKitDocument* pThis,
2944  unsigned char* pBuffer,
2945  const int nCanvasWidth, const int nCanvasHeight,
2946  const int nTilePosX, const int nTilePosY,
2947  const int nTileWidth, const int nTileHeight)
2948 {
2949  comphelper::ProfileZone aZone("doc_paintTile");
2950 
2951  SolarMutexGuard aGuard;
2953 
2954  SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
2955  "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
2956  nCanvasWidth << "x" << nCanvasHeight << "]px" );
2957 
2958  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2959  if (!pDoc)
2960  {
2961  SetLastExceptionMsg("Document doesn't support tiled rendering");
2962  return;
2963  }
2964 
2965 #if defined(UNX) && !defined(MACOSX) && !defined(ENABLE_HEADLESS)
2966 
2967  // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
2968  // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
2969  // everything is painted bigger or smaller. This is different to what Calc's internal scaling
2970  // would do - because that one is trying to fit the lines between cells to integer multiples of
2971  // pixels.
2973 
2974 #if defined(IOS)
2975  double fDPIScaleX = 1.0;
2976  paintTileIOS(pThis, pBuffer, nCanvasWidth, nCanvasHeight, fDPIScaleX, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2977 #else
2978  ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::DEFAULT);
2979 
2980 #if !defined(ANDROID) || HAVE_FEATURE_ANDROID_LOK
2981  // Don't set the transparent background in the 'old' (JNI-based) Android
2982  // app - no idea why it needs avoiding this.
2983  // Set background to transparent by default.
2984  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2985 #endif
2986 
2987  pDevice->SetOutputSizePixelScaleOffsetAndBuffer(
2988  Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
2989  pBuffer);
2990 
2991  pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
2992  nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2993 
2994  static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
2995  if (bDebug)
2996  {
2997  // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
2998  tools::Rectangle aRect(0, 0, 5, 5);
2999  aRect = pDevice->PixelToLogic(aRect);
3000  pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
3001  pDevice->SetFillColor(COL_LIGHTRED);
3002  pDevice->SetLineColor();
3003  pDevice->DrawRect(aRect);
3004  pDevice->Pop();
3005  }
3006 #endif
3007 
3008 #else
3009  (void) pBuffer;
3010 #endif
3011 }
3012 
3013 #ifdef IOS
3014 
3015 // This function is separate only to be used by LibreOfficeLight. If that app can be retired, this
3016 // function's code can be inlined.
3017 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
3018  void* rCGContext,
3019  const int nCanvasWidth, const int nCanvasHeight,
3020  const int nTilePosX, const int nTilePosY,
3021  const int nTileWidth, const int nTileHeight)
3022 {
3023  SolarMutexGuard aGuard;
3025 
3026  SAL_INFO( "lok.tiledrendering", "paintTileToCGContext: painting [" << nTileWidth << "x" << nTileHeight <<
3027  "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3028  nCanvasWidth << "x" << nCanvasHeight << "]px" );
3029 
3030  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3031  if (!pDoc)
3032  {
3033  SetLastExceptionMsg("Document doesn't support tiled rendering");
3034  return;
3035  }
3036 
3037  Size aCanvasSize(nCanvasWidth, nCanvasHeight);
3038  paintTileToCGContext(pDoc, rCGContext, aCanvasSize, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3039 }
3040 
3041 #endif
3042 
3043 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
3044  unsigned char* pBuffer,
3045  const int nPart,
3046  const int nCanvasWidth, const int nCanvasHeight,
3047  const int nTilePosX, const int nTilePosY,
3048  const int nTileWidth, const int nTileHeight)
3049 {
3050  comphelper::ProfileZone aZone("doc_paintPartTile");
3051 
3052  SolarMutexGuard aGuard;
3054 
3055  SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " ["
3056  << nTileWidth << "x" << nTileHeight << "]@("
3057  << nTilePosX << ", " << nTilePosY << ") to ["
3058  << nCanvasWidth << "x" << nCanvasHeight << "]px" );
3059 
3060  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3061  int nOrigViewId = doc_getView(pThis);
3062 
3063  if (nOrigViewId < 0)
3064  {
3065  // tile painting always needs a SfxViewShell::Current(), but actually
3066  // it does not really matter which one - all of them should paint the
3067  // same thing.
3068  int viewCount = doc_getViewsCount(pThis);
3069  if (viewCount == 0)
3070  return;
3071 
3072  std::vector<int> viewIds(viewCount);
3073  doc_getViewIds(pThis, viewIds.data(), viewCount);
3074 
3075  nOrigViewId = viewIds[0];
3076  doc_setView(pThis, nOrigViewId);
3077  }
3078 
3079  // Disable callbacks while we are painting.
3080  if (nOrigViewId >= 0)
3081  {
3082  const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
3083  if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
3084  handlerIt->second->disableCallbacks();
3085  }
3086 
3087  try
3088  {
3089  // Text documents have a single coordinate system; don't change part.
3090  int nOrigPart = 0;
3091  const bool isText = (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT);
3092  int nViewId = nOrigViewId;
3093  if (!isText)
3094  {
3095  // Check if just switching to another view is enough, that has
3096  // less side-effects.
3097  if (nPart != doc_getPart(pThis))
3098  {
3099  SfxViewShell* pViewShell = SfxViewShell::GetFirst();
3100  while (pViewShell)
3101  {
3102  if (pViewShell->getPart() == nPart)
3103  {
3104  nViewId = static_cast<sal_Int32>(pViewShell->GetViewShellId());
3105  doc_setView(pThis, nViewId);
3106  break;
3107  }
3108  pViewShell = SfxViewShell::GetNext(*pViewShell);
3109  }
3110  }
3111 
3112  nOrigPart = doc_getPart(pThis);
3113  if (nPart != nOrigPart)
3114  {
3115  doc_setPart(pThis, nPart);
3116  }
3117  }
3118 
3119  doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3120 
3121  if (!isText && nPart != nOrigPart)
3122  {
3123  doc_setPart(pThis, nOrigPart);
3124  }
3125  if (!isText && nViewId != nOrigViewId)
3126  {
3127  doc_setView(pThis, nOrigViewId);
3128  }
3129  }
3130  catch (const std::exception&)
3131  {
3132  // Nothing to do but restore the PartTilePainting flag.
3133  }
3134 
3135  if (nOrigViewId >= 0)
3136  {
3137  const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
3138  if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
3139  handlerIt->second->enableCallbacks();
3140  }
3141 }
3142 
3143 static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
3144 {
3146  return LOK_TILEMODE_BGRA;
3147 }
3148 
3149 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
3150  long* pWidth,
3151  long* pHeight)
3152 {
3153  comphelper::ProfileZone aZone("doc_getDocumentSize");
3154 
3155  SolarMutexGuard aGuard;
3157 
3158  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3159  if (pDoc)
3160  {
3161  Size aDocumentSize = pDoc->getDocumentSize();
3162  *pWidth = aDocumentSize.Width();
3163  *pHeight = aDocumentSize.Height();
3164  }
3165  else
3166  {
3167  SetLastExceptionMsg("Document doesn't support tiled rendering");
3168  }
3169 }
3170 
3171 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
3172  const char* pArguments)
3173 {
3174  comphelper::ProfileZone aZone("doc_initializeForRendering");
3175 
3176  SolarMutexGuard aGuard;
3178 
3179  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3180  if (pDoc)
3181  {
3185  }
3186 }
3187 
3188 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
3189  LibreOfficeKitCallback pCallback,
3190  void* pData)
3191 {
3192  SolarMutexGuard aGuard;
3194 
3195  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3196 
3197  int nView = SfxLokHelper::getView();
3198  if (nView < 0)
3199  return;
3200 
3201  if (pCallback != nullptr)
3202  {
3203  size_t nId = nView;
3204  for (auto& pair : pDocument->mpCallbackFlushHandlers)
3205  {
3206  if (pair.first == nId)
3207  continue;
3208 
3209  pair.second->addViewStates(nView);
3210  }
3211  }
3212  else
3213  {
3214  size_t nId = nView;
3215  for (auto& pair : pDocument->mpCallbackFlushHandlers)
3216  {
3217  if (pair.first == nId)
3218  continue;
3219 
3220  pair.second->removeViewStates(nView);
3221  }
3222  }
3223 
3224  pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData);
3225 
3226  if (pCallback != nullptr)
3227  {
3228  size_t nId = nView;
3229  for (const auto& pair : pDocument->mpCallbackFlushHandlers)
3230  {
3231  if (pair.first == nId)
3232  continue;
3233 
3234  pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
3235  }
3236  }
3237 
3238  if (SfxViewShell* pViewShell = SfxViewShell::Current())
3239  {
3241  }
3242 }
3243 
3245 static char* getPostIts(LibreOfficeKitDocument* pThis)
3246 {
3248  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3249  if (!pDoc)
3250  {
3251  SetLastExceptionMsg("Document doesn't support tiled rendering");
3252  return nullptr;
3253  }
3254  OUString aComments = pDoc->getPostIts();
3255  return strdup(aComments.toUtf8().getStr());
3256 }
3257 
3259 static char* getPostItsPos(LibreOfficeKitDocument* pThis)
3260 {
3262  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3263  if (!pDoc)
3264  {
3265  SetLastExceptionMsg("Document doesn't support tiled rendering");
3266  return nullptr;
3267  }
3268  OUString aComments = pDoc->getPostItsPos();
3269  return strdup(aComments.toUtf8().getStr());
3270 }
3271 
3272 static char* getRulerState(LibreOfficeKitDocument* pThis)
3273 {
3275  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3276  if (!pDoc)
3277  {
3278  SetLastExceptionMsg("Document doesn't support tiled rendering");
3279  return nullptr;
3280  }
3281  OUString state = pDoc->getRulerState();
3282  return strdup(state.toUtf8().getStr());
3283 }
3284 
3285 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
3286 {
3287  comphelper::ProfileZone aZone("doc_postKeyEvent");
3288 
3289  SolarMutexGuard aGuard;
3291 
3292  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3293  if (!pDoc)
3294  {
3295  SetLastExceptionMsg("Document doesn't support tiled rendering");
3296  return;
3297  }
3298 
3299  try
3300  {
3301  pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
3302  }
3303  catch (const uno::Exception& exception)
3304  {
3305  SetLastExceptionMsg(exception.Message);
3306  SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message);
3307  }
3308 }
3309 
3310 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
3311 {
3312  comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
3313 
3314  SolarMutexGuard aGuard;
3315  VclPtr<vcl::Window> pWindow;
3316  if (nWindowId == 0)
3317  {
3318  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3319  if (!pDoc)
3320  {
3321  SetLastExceptionMsg("Document doesn't support tiled rendering");
3322  return;
3323  }
3324  pWindow = pDoc->getDocWindow();
3325  }
3326  else
3327  {
3328  pWindow = vcl::Window::FindLOKWindow(nWindowId);
3329  }
3330 
3331  if (!pWindow)
3332  {
3333  SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
3334  return;
3335  }
3336 
3337  SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(OString(pText, strlen(pText))));
3338 }
3339 
3340 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
3341 {
3342  SolarMutexGuard aGuard;
3343  VclPtr<vcl::Window> pWindow;
3344  if (nLOKWindowId == 0)
3345  {
3346  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3347  if (!pDoc)
3348  {
3349  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3350  return;
3351  }
3352  pWindow = pDoc->getDocWindow();
3353  }
3354  else
3355  {
3356  pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3357  }
3358 
3359  if (!pWindow)
3360  {
3361  gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
3362  return;
3363  }
3364 
3365  // Annoyingly - backspace and delete are handled in the apps via an accelerator
3366  // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
3367  // order we do this synchronously here, unless we're in a dialog.
3368  if (nCharBefore > 0)
3369  {
3370  // backspace
3371  if (nLOKWindowId == 0)
3372  {
3373  KeyEvent aEvt(8, 1283);
3374  for (int i = 0; i < nCharBefore; ++i)
3375  pWindow->KeyInput(aEvt);
3376  }
3377  else
3378  SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
3379  }
3380 
3381  if (nCharAfter > 0)
3382  {
3383  // delete (forward)
3384  if (nLOKWindowId == 0)
3385  {
3386  KeyEvent aEvt(46, 1286);
3387  for (int i = 0; i < nCharAfter; ++i)
3388  pWindow->KeyInput(aEvt);
3389  }
3390  else
3391  SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
3392  }
3393 }
3394 
3395 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
3396 {
3397  comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
3398 
3399  SolarMutexGuard aGuard;
3401 
3402  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3403  if (!pWindow)
3404  {
3405  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3406  return;
3407  }
3408 
3409  KeyEvent aEvent(nCharCode, nKeyCode, 0);
3410 
3411  switch (nType)
3412  {
3413  case LOK_KEYEVENT_KEYINPUT:
3414  Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
3415  break;
3416  case LOK_KEYEVENT_KEYUP:
3417  Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
3418  break;
3419  default:
3420  assert(false);
3421  break;
3422  }
3423 }
3424 
3425 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
3426 {
3427  comphelper::ProfileZone aZone("doc_renderShapeSelection");
3428 
3429  SolarMutexGuard aGuard;
3431 
3432  LokChartHelper aChartHelper(SfxViewShell::Current());
3433 
3434  if (aChartHelper.GetWindow())
3435  return 0;
3436 
3437  try
3438  {
3439  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3440 
3441  uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3442 
3443  SvMemoryStream aOutStream;
3444  uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream);
3445 
3446  utl::MediaDescriptor aMediaDescriptor;
3447  switch (doc_getDocumentType(pThis))
3448  {
3449  case LOK_DOCTYPE_PRESENTATION:
3450  aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
3451  break;
3452  case LOK_DOCTYPE_TEXT:
3453  aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
3454  break;
3455  case LOK_DOCTYPE_SPREADSHEET:
3456  aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
3457  break;
3458  default:
3459  SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
3460  }
3461  aMediaDescriptor["SelectionOnly"] <<= true;
3462  aMediaDescriptor["OutputStream"] <<= xOut;
3463 
3464  xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
3465 
3466  if (pOutput)
3467  {
3468  const size_t nOutputSize = aOutStream.GetEndOfData();
3469  *pOutput = static_cast<char*>(malloc(nOutputSize));
3470  if (*pOutput)
3471  {
3472  std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
3473  return nOutputSize;
3474  }
3475  }
3476  }
3477  catch (const uno::Exception& exception)
3478  {
3479  css::uno::Any exAny( cppu::getCaughtException() );
3480  SetLastExceptionMsg(exception.Message);
3481  SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
3482  }
3483 
3484  return 0;
3485 }
3486 
3487 namespace {
3488 
3496 class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
3497 {
3498  OString maCommand;
3499  std::shared_ptr<CallbackFlushHandler> mpCallback;
3500 
3501 public:
3502  DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> const & pCallback)
3503  : maCommand(pCommand)
3504  , mpCallback(pCallback)
3505  {
3506  assert(mpCallback);
3507  }
3508 
3509  virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
3510  {
3511  boost::property_tree::ptree aTree;
3512  aTree.put("commandName", maCommand.getStr());
3513 
3514  if (rEvent.State != frame::DispatchResultState::DONTKNOW)
3515  {
3516  bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
3517  aTree.put("success", bSuccess);
3518  }
3519 
3520  aTree.add_child("result", unoAnyToPropertyTree(rEvent.Result));
3521 
3522  std::stringstream aStream;
3523  boost::property_tree::write_json(aStream, aTree);
3524  OString aPayload = aStream.str().c_str();
3525  mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3526  }
3527 
3528  virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
3529 };
3530 
3531 } // anonymous namespace
3532 
3533 static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nWindowId, const char* pArguments)
3534 {
3535  SolarMutexGuard aGuard;
3536 
3537  StringMap aMap(jsonToStringMap(pArguments));
3538  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nWindowId);
3539 
3540  if (!pWindow && nWindowId >= 1000000000 /* why unsigned? */)
3541  pWindow = getSidebarWindow();
3542 
3543  if (!pWindow)
3544  {
3545  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3546  return;
3547  }
3548  else if (aMap.find("id") != aMap.end())
3549  {
3550  const OUString sClickAction("CLICK");
3551  const OUString sSelectAction("SELECT");
3552  const OUString sClearAction("CLEAR");
3553  const OUString sTypeAction("TYPE");
3554  const OUString sUpAction("UP");
3555  const OUString sDownAction("DOWN");
3556 
3557  try
3558  {
3559  WindowUIObject aUIObject(pWindow);
3560  std::unique_ptr<UIObject> pUIWindow(aUIObject.get_child(aMap["id"]));
3561  if (pUIWindow) {
3562  bool bIsClickAction = false;
3563 
3564  if (aMap.find("cmd") != aMap.end()) {
3565  if (aMap["cmd"] == "selected")
3566  {
3567  aMap["POS"] = aMap["data"];
3568  aMap["TEXT"] = aMap["data"];
3569 
3570  pUIWindow->execute(sSelectAction, aMap);
3571  }
3572  else if (aMap["cmd"] == "plus")
3573  {
3574  pUIWindow->execute(sUpAction, aMap);
3575  }
3576  else if (aMap["cmd"] == "minus")
3577  {
3578  pUIWindow->execute(sDownAction, aMap);
3579  }
3580  else if (aMap["cmd"] == "set")
3581  {
3582  aMap["TEXT"] = aMap["data"];
3583 
3584  pUIWindow->execute(sClearAction, aMap);
3585  pUIWindow->execute(sTypeAction, aMap);
3586  }
3587  else
3588  bIsClickAction = true;
3589  }
3590  else
3591  bIsClickAction = true;
3592 
3593  if (bIsClickAction)
3594  pUIWindow->execute(sClickAction, aMap);
3595  }
3596  } catch(...) {}
3597 
3598  // force resend
3599  pWindow->Resize();
3600  }
3601 }
3602 
3603 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
3604 {
3605  comphelper::ProfileZone aZone("doc_postUnoCommand");
3606 
3607  SolarMutexGuard aGuard;
3609 
3611  OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
3612  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3613 
3614  std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
3615 
3616  if (!vcl::lok::isUnipoll())
3617  {
3618  beans::PropertyValue aSynchronMode;
3619  aSynchronMode.Name = "SynchronMode";
3620  aSynchronMode.Value <<= false;
3621  aPropertyValuesVector.push_back(aSynchronMode);
3622  }
3623 
3624  int nView = SfxLokHelper::getView();
3625  if (nView < 0)
3626  return;
3627 
3628  if (gImpl && (aCommand == ".uno:LOKSetMobile" || aCommand == ".uno:LOKSetMobilePhone"))
3629  {
3631  return;
3632  }
3633  else if (gImpl && aCommand == ".uno:LOKSetTablet")
3634  {
3636  return;
3637  }
3638  else if (gImpl && aCommand == ".uno:ToggleOrientation")
3639  {
3640  ExecuteOrientationChange();
3641  return;
3642  }
3643  else if (gImpl && aCommand == ".uno:Text")
3644  {
3645  aCommand = ".uno:Text?CreateDirectly:bool=true";
3646  }
3647 
3648  // handle potential interaction
3649  if (gImpl && aCommand == ".uno:Save")
3650  {
3651  rtl::Reference<LOKInteractionHandler> const pInteraction(
3652  new LOKInteractionHandler("save", gImpl, pDocument));
3653  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction.get());
3654 
3655  beans::PropertyValue aValue;
3656  aValue.Name = "InteractionHandler";
3657  aValue.Value <<= xInteraction;
3658  aPropertyValuesVector.push_back(aValue);
3659 
3660  bool bDontSaveIfUnmodified = false;
3661  aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
3662  aPropertyValuesVector.end(),
3663  [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
3664  if (aItem.Name == "DontSaveIfUnmodified")
3665  {
3666  bDontSaveIfUnmodified = aItem.Value.get<bool>();
3667  return true;
3668  }
3669  return false;
3670  }), aPropertyValuesVector.end());
3671 
3672  // skip saving and tell the result via UNO_COMMAND_RESULT
3673  if (bDontSaveIfUnmodified && !pDocSh->IsModified())
3674  {
3675  boost::property_tree::ptree aTree;
3676  aTree.put("commandName", pCommand);
3677  aTree.put("success", false);
3678 
3679  // Add the reason for not saving
3680  const uno::Any aResultValue = uno::makeAny(OUString("unmodified"));
3681  aTree.add_child("result", unoAnyToPropertyTree(aResultValue));
3682 
3683  std::stringstream aStream;
3684  boost::property_tree::write_json(aStream, aTree);
3685  OString aPayload = aStream.str().c_str();
3686  pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3687  return;
3688  }
3689  }
3690  else if (gImpl && aCommand == ".uno:TransformDialog")
3691  {
3692  bool bNeedConversion = false;
3693  SfxViewShell* pViewShell = SfxViewShell::Current();
3694  LokChartHelper aChartHelper(pViewShell);
3695 
3696  if (aChartHelper.GetWindow() )
3697  {
3698  bNeedConversion = true;
3699  }
3700  else if (const SdrView* pView = pViewShell->GetDrawView())
3701  {
3702  if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
3703  {
3704  bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
3705  }
3706  }
3707 
3708  if (bNeedConversion)
3709  {
3710  sal_Int32 value;
3711  for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
3712  {
3713  if (rPropValue.Name == "TransformPosX"
3714  || rPropValue.Name == "TransformPosY"
3715  || rPropValue.Name == "TransformWidth"
3716  || rPropValue.Name == "TransformHeight"
3717  || rPropValue.Name == "TransformRotationX"
3718  || rPropValue.Name == "TransformRotationY")
3719  {
3720  rPropValue.Value >>= value;
3721  value = OutputDevice::LogicToLogic(value, MapUnit::MapTwip, MapUnit::Map100thMM);
3722  rPropValue.Value <<= value;
3723  }
3724  }
3725  }
3726 
3727  if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0)
3728  {
3729  if (aPropertyValuesVector[0].Name != "Action")
3730  {
3731  tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
3732  int nLeft = OutputDevice::LogicToLogic(aChartBB.Left(), MapUnit::MapTwip, MapUnit::Map100thMM);
3733  int nTop = OutputDevice::LogicToLogic(aChartBB.Top(), MapUnit::MapTwip, MapUnit::Map100thMM);
3734 
3735  for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
3736  {
3737  if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
3738  {
3739  auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
3740  rPropValue.Value <<= value - nLeft;
3741  }
3742  else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
3743  {
3744  auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
3745  rPropValue.Value <<= value - nTop;
3746  }
3747  }
3748  }
3749  util::URL aCommandURL;
3750  aCommandURL.Path = "LOKTransform";
3751  css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
3752  aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
3753  return;
3754  }
3755  }
3756  else if (gImpl && aCommand == ".uno:SidebarShow")
3757  {
3758  setupSidebar(true);
3759  return;
3760  }
3761  else if (gImpl && aCommand == ".uno:SidebarHide")
3762  {
3763  setupSidebar(false);
3764  return;
3765  }
3766 
3767  bool bResult = false;
3768  LokChartHelper aChartHelper(SfxViewShell::Current());
3769 
3770  if (aChartHelper.GetWindow() )
3771  {
3772  util::URL aCommandURL;
3773  aCommandURL.Path = aCommand.copy(5);
3774  css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
3775  aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
3776  return;
3777  }
3778  else if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
3779  {
3780  bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
3781  new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
3782  }
3783  else
3784  bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
3785 
3786  if (!bResult)
3787  {
3788  SetLastExceptionMsg("Failed to dispatch " + aCommand);
3789  }
3790 }
3791 
3792 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
3793 {
3794  comphelper::ProfileZone aZone("doc_postMouseEvent");
3795 
3796  SolarMutexGuard aGuard;
3798 
3799  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3800  if (!pDoc)
3801  {
3802  SetLastExceptionMsg("Document doesn't support tiled rendering");
3803  return;
3804  }
3805  try
3806  {
3807  pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
3808  }
3809  catch (const uno::Exception& exception)
3810  {
3811  SetLastExceptionMsg(exception.Message);
3812  SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message);
3813  }
3814 }
3815 
3816 static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
3817 {
3818  comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
3819 
3820  SolarMutexGuard aGuard;
3822 
3823  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3824  if (!pWindow)
3825  {
3826  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3827  return;
3828  }
3829 
3830  Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
3831  Point aPos(nX, nY);
3832  aPos.Move(aOffset);
3833  MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
3834 
3835  vcl::EnableDialogInput(pWindow);
3836 
3837  switch (nType)
3838  {
3839  case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
3840  Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
3841  break;
3842  case LOK_MOUSEEVENT_MOUSEBUTTONUP:
3843  Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
3844  break;
3845  case LOK_MOUSEEVENT_MOUSEMOVE:
3846  Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
3847  break;
3848  default:
3849  assert(false);
3850  break;
3851  }
3852 }
3853 
3854 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
3855 {
3856  comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
3857 
3858  SolarMutexGuard aGuard;
3860 
3861  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3862  if (!pWindow)
3863  {
3864  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3865  return;
3866  }
3867 
3868  OString aType(pType);
3869  GestureEventType eEventType = GestureEventType::PanningUpdate;
3870 
3871  if (aType == "panBegin")
3872  eEventType = GestureEventType::PanningBegin;
3873  else if (aType == "panEnd")
3874  eEventType = GestureEventType::PanningEnd;
3875 
3877  sal_Int32(nX),
3878  sal_Int32(nY),
3879  eEventType,
3880  sal_Int32(nOffset),
3881  PanningOrientation::Vertical,
3882  };
3883 
3884  vcl::EnableDialogInput(pWindow);
3885 
3886  Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
3887 }
3888 
3889 static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
3890 {
3891  comphelper::ProfileZone aZone("doc_setTextSelection");
3892 
3893  SolarMutexGuard aGuard;
3895 
3896  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3897  if (!pDoc)
3898  {
3899  SetLastExceptionMsg("Document doesn't support tiled rendering");
3900  return;
3901  }
3902 
3903  pDoc->setTextSelection(nType, nX, nY);
3904 }
3905 
3906 static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY)
3907 {
3908  comphelper::ProfileZone aZone("doc_setWindowTextSelection");
3909 
3910  SolarMutexGuard aGuard;
3912 
3913  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3914  if (!pWindow)
3915  {
3916  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3917  return;
3918  }
3919 
3920 
3921  Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
3922  Point aCursorPos(nX, nY);
3923  aCursorPos.Move(aOffset);
3924  sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT;
3925 
3926  MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier);
3927  Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent);
3928  Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent);
3929 }
3930 
3931 static bool getFromTransferrable(
3932  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3933  const OString &aInMimeType, OString &aRet);
3934 
3935 static bool encodeImageAsHTML(
3936  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3937  const OString &aMimeType, OString &aRet)
3938 {
3939  if (!getFromTransferrable(xTransferable, aMimeType, aRet))
3940  return false;
3941 
3942  // Encode in base64.
3943  auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
3944  aRet.getLength());
3945  OUStringBuffer aBase64Data;
3946  comphelper::Base64::encode(aBase64Data, aSeq);
3947 
3948  // Embed in HTML.
3949  aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
3950  "<html><head>"
3951  "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
3952  "name=\"generator\" content=\""
3953  + getGenerator().toUtf8()
3954  + "\"/>"
3955  "</head><body><img src=\"data:" + aMimeType + ";base64,"
3956  + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>";
3957 
3958  return true;
3959 }
3960 
3961 static bool encodeTextAsHTML(
3962  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3963  const OString &aMimeType, OString &aRet)
3964 {
3965  if (!getFromTransferrable(xTransferable, aMimeType, aRet))
3966  return false;
3967 
3968  // Embed in HTML - FIXME: needs some escaping.
3969  aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
3970  "<html><head>"
3971  "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
3972  "name=\"generator\" content=\""
3973  + getGenerator().toUtf8()
3974  + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
3975 
3976  return true;
3977 }
3978 
3980  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
3981  const OString &aInMimeType, OString &aRet)
3982 {
3983  OString aMimeType(aInMimeType);
3984 
3985  // Take care of UTF-8 text here.
3986  bool bConvert = false;
3987  sal_Int32 nIndex = 0;
3988  if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
3989  {
3990  if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
3991  {
3992  aMimeType = "text/plain;charset=utf-16";
3993  bConvert = true;
3994  }
3995  }
3996 
3997  datatransfer::DataFlavor aFlavor;
3998  aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
3999  if (aMimeType == "text/plain;charset=utf-16")
4000  aFlavor.DataType = cppu::UnoType<OUString>::get();
4001  else
4002  aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
4003 
4004  if (!xTransferable->isDataFlavorSupported(aFlavor))
4005  {
4006  // Try harder for HTML it is our copy/paste meta-file format
4007  if (aInMimeType == "text/html")
4008  {
4009  // Desperate measures - convert text to HTML instead.
4010  if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
4011  return true;
4012  // If html is not supported, might be a graphic-selection,
4013  if (encodeImageAsHTML(xTransferable, "image/png", aRet))
4014  return true;
4015  }
4016 
4017  SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
4018  return false;
4019  }
4020 
4021  uno::Any aAny;
4022  try
4023  {
4024  aAny = xTransferable->getTransferData(aFlavor);
4025  }
4026  catch (const css::datatransfer::UnsupportedFlavorException& e)
4027  {
4028  SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
4029  return false;
4030  }
4031  catch (const css::uno::Exception& e)
4032  {
4033  SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
4034  return false;
4035  }
4036 
4037  if (aFlavor.DataType == cppu::UnoType<OUString>::get())
4038  {
4039  OUString aString;
4040  aAny >>= aString;
4041  if (bConvert)
4042  aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
4043  else
4044  aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
4045  }
4046  else
4047  {
4048  uno::Sequence<sal_Int8> aSequence;
4049  aAny >>= aSequence;
4050  aRet = OString(reinterpret_cast<char*>(aSequence.getArray()), aSequence.getLength());
4051  }
4052 
4053  return true;
4054 }
4055 
4056 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
4057 {
4058  comphelper::ProfileZone aZone("doc_getTextSelection");
4059 
4060  SolarMutexGuard aGuard;
4062 
4063  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4064  if (!pDoc)
4065  {
4066  SetLastExceptionMsg("Document doesn't support tiled rendering");
4067  return nullptr;
4068  }
4069 
4070  css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
4071  if (!xTransferable)
4072  {
4073  SetLastExceptionMsg("No selection available");
4074  return nullptr;
4075  }
4076 
4077  const char *pType = pMimeType;
4078  if (!pType || pType[0] == '\0')
4079  pType = "text/plain;charset=utf-8";
4080 
4081  OString aRet;
4082  bool bSuccess = getFromTransferrable(xTransferable, OString(pType), aRet);
4083  if (!bSuccess)
4084  return nullptr;
4085 
4086  if (pUsedMimeType) // legacy
4087  {
4088  if (pMimeType)
4089  *pUsedMimeType = strdup(pMimeType);
4090  else
4091  *pUsedMimeType = nullptr;
4092  }
4093 
4094  return convertOString(aRet);
4095 }
4096 
4097 static int doc_getSelectionType(LibreOfficeKitDocument* pThis)
4098 {
4099  comphelper::ProfileZone aZone("doc_getSelectionType");
4100 
4101  SolarMutexGuard aGuard;
4103 
4104  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4105  if (!pDoc)
4106  {
4107  SetLastExceptionMsg("Document doesn't support tiled rendering");
4108  return LOK_SELTYPE_NONE;
4109  }
4110 
4111  css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(pDoc->getSelection(), css::uno::UNO_QUERY);
4112  if (!xTransferable)
4113  {
4114  SetLastExceptionMsg("No selection available");
4115  return LOK_SELTYPE_NONE;
4116  }
4117 
4118  if (xTransferable->isComplex())
4119  return LOK_SELTYPE_COMPLEX;
4120 
4121  OString aRet;
4122  bool bSuccess = getFromTransferrable(xTransferable, "text/plain;charset=utf-8", aRet);
4123  if (!bSuccess)
4124  return LOK_SELTYPE_NONE;
4125 
4126  if (aRet.getLength() > 10000)
4127  return LOK_SELTYPE_COMPLEX;
4128 
4129  return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE;
4130 }
4131 
4132 static int doc_getClipboard(LibreOfficeKitDocument* pThis,
4133  const char **pMimeTypes,
4134  size_t *pOutCount,
4135  char ***pOutMimeTypes,
4136  size_t **pOutSizes,
4137  char ***pOutStreams)
4138 {
4139  comphelper::ProfileZone aZone("doc_getClipboard");
4140 
4141  SolarMutexGuard aGuard;
4143 
4144  assert (pOutCount);
4145  assert (pOutMimeTypes);
4146  assert (pOutSizes);
4147  assert (pOutStreams);
4148 
4149  *pOutCount = 0;
4150  *pOutMimeTypes = nullptr;
4151  *pOutSizes = nullptr;
4152  *pOutStreams = nullptr;
4153 
4154  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4155  if (!pDoc)
4156  {
4157  SetLastExceptionMsg("Document doesn't support tiled rendering");
4158  return 0;
4159  }
4160 
4162 
4163  css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents();
4164  SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferrable: " << xTransferable);
4165  if (!xTransferable)
4166  {
4167  SetLastExceptionMsg("No clipboard content available");
4168  return 0;
4169  }
4170 
4171  std::vector<OString> aMimeTypes;
4172  if (!pMimeTypes) // everything
4173  {
4174  const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors();
4175  if (!flavors.getLength())
4176  {
4177  SetLastExceptionMsg("Flavourless selection");
4178  return 0;
4179  }
4180  for (const auto &it : flavors)
4181  aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8));
4182  }
4183  else
4184  {
4185  for (size_t i = 0; pMimeTypes[i]; ++i)
4186  aMimeTypes.push_back(OString(pMimeTypes[i]));
4187  }
4188 
4189  *pOutCount = aMimeTypes.size();
4190  *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t)));
4191  *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
4192  *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
4193  for (size_t i = 0; i < aMimeTypes.size(); ++i)
4194  {
4195  if (aMimeTypes[i] == "text/plain;charset=utf-16")
4196  (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8");
4197  else
4198  (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr());
4199 
4200  OString aRet;
4201  bool bSuccess = getFromTransferrable(xTransferable, (*pOutMimeTypes)[i], aRet);
4202  if (!bSuccess || aRet.getLength() < 1)
4203  {
4204  (*pOutSizes)[i] = 0;
4205  (*pOutStreams)[i] = nullptr;
4206  }
4207  else
4208  {
4209  (*pOutSizes)[i] = aRet.getLength();
4210  (*pOutStreams)[i] = convertOString(aRet);
4211  }
4212  }
4213 
4214  return 1;
4215 }
4216 
4217 static int doc_setClipboard(LibreOfficeKitDocument* pThis,
4218  const size_t nInCount,
4219  const char **pInMimeTypes,
4220  const size_t *pInSizes,
4221  const char **pInStreams)
4222 {
4223 #ifdef IOS
4224  (void) pThis;
4225  (void) nInCount;
4226  (void) pInMimeTypes;
4227  (void) pInSizes;
4228  (void) pInStreams;
4229 #else
4230  comphelper::ProfileZone aZone("doc_setClipboard");
4231 
4232  SolarMutexGuard aGuard;
4234 
4235  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4236  if (!pDoc)
4237  {
4238  SetLastExceptionMsg("Document doesn't support tiled rendering");
4239  return false;
4240  }
4241 
4242  uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams));
4243 
4244  auto xClip = forceSetClipboardForCurrentView(pThis);
4245  xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
4246 
4247  SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable);
4248 
4249  if (!pDoc->isMimeTypeSupported())
4250  {
4251  SetLastExceptionMsg("Document doesn't support this mime type");
4252  return false;
4253  }
4254 #endif
4255  return true;
4256 }
4257 
4258 static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize)
4259 {
4260  comphelper::ProfileZone aZone("doc_paste");
4261 
4262  SolarMutexGuard aGuard;
4263 
4264  const char *pInMimeTypes[1];
4265  const char *pInStreams[1];
4266  size_t pInSizes[1];
4267  pInMimeTypes[0] = pMimeType;
4268  pInSizes[0] = nSize;
4269  pInStreams[0] = pData;
4270 
4271  if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams))
4272  return false;
4273 
4274  uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
4275  {
4276  {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AS_CHARACTER))},
4277  {"IgnoreComments", uno::makeAny(true)},
4278  }));
4279  if (!comphelper::dispatchCommand(".uno:Paste", aPropertyValues))
4280  {
4281  SetLastExceptionMsg("Failed to dispatch the .uno: command");
4282  return false;
4283  }
4284 
4285  return true;
4286 }
4287 
4288 static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
4289 {
4290  comphelper::ProfileZone aZone("doc_setGraphicSelection");
4291 
4292  SolarMutexGuard aGuard;
4294 
4295  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4296  if (!pDoc)
4297  {
4298  SetLastExceptionMsg("Document doesn't support tiled rendering");
4299  return;
4300  }
4301 
4302  pDoc->setGraphicSelection(nType, nX, nY);
4303 }
4304 
4305 static void doc_resetSelection(LibreOfficeKitDocument* pThis)
4306 {
4307  comphelper::ProfileZone aZone("doc_resetSelection");
4308 
4309  SolarMutexGuard aGuard;
4311 
4312  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4313  if (!pDoc)
4314  {
4315  SetLastExceptionMsg("Document doesn't support tiled rendering");
4316  return;
4317  }
4318 
4319  pDoc->resetSelection();
4320 }
4321 
4322 static char* getLanguages(const char* pCommand)
4323 {
4324  css::uno::Sequence< css::lang::Locale > aLocales;
4325 
4326  if (xContext.is())
4327  {
4328  css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
4329  if (xLangSrv.is())
4330  {
4331  css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
4332  if (xSpell.is())
4333  aLocales = xSpell->getLocales();
4334  }
4335  }
4336 
4337  boost::property_tree::ptree aTree;
4338  aTree.put("commandName", pCommand);
4339  boost::property_tree::ptree aValues;
4340  boost::property_tree::ptree aChild;
4341  OUString sLanguage;
4342  for ( sal_Int32 itLocale = 0; itLocale < aLocales.getLength(); itLocale++ )
4343  {
4344  const LanguageTag aLanguageTag( aLocales[itLocale]);
4345  sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
4346  if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
4347  continue;
4348 
4349  sLanguage += ";" + aLanguageTag.getBcp47(false);
4350  aChild.put("", sLanguage.toUtf8());
4351  aValues.push_back(std::make_pair("", aChild));
4352  }
4353  aTree.add_child("commandValues", aValues);
4354  std::stringstream aStream;
4355  boost::property_tree::write_json(aStream, aTree);
4356  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4357  assert(pJson); // Don't handle OOM conditions
4358  strcpy(pJson, aStream.str().c_str());
4359  pJson[aStream.str().size()] = '\0';
4360  return pJson;
4361 }
4362 
4363 static char* getFonts (const char* pCommand)
4364 {
4366  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4367  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4368  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4369 
4370  boost::property_tree::ptree aTree;
4371  aTree.put("commandName", pCommand);
4372  boost::property_tree::ptree aValues;
4373  if ( pList )
4374  {
4375  sal_uInt16 nFontCount = pList->GetFontNameCount();
4376  for (sal_uInt16 i = 0; i < nFontCount; ++i)
4377  {
4378  boost::property_tree::ptree aChildren;
4379  const FontMetric& rFontMetric = pList->GetFontName(i);
4380  const sal_IntPtr* pAry = pList->GetSizeAry(rFontMetric);
4381  sal_uInt16 nSizeCount = 0;
4382  while (pAry[nSizeCount])
4383  {
4384  boost::property_tree::ptree aChild;
4385  aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10);
4386  aChildren.push_back(std::make_pair("", aChild));
4387  nSizeCount++;
4388  }
4389  aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren);
4390  }
4391  }
4392  aTree.add_child("commandValues", aValues);
4393  std::stringstream aStream;
4394  boost::property_tree::write_json(aStream, aTree);
4395  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4396  assert(pJson); // Don't handle OOM conditions
4397  strcpy(pJson, aStream.str().c_str());
4398  pJson[aStream.str().size()] = '\0';
4399  return pJson;
4400 }
4401 
4402 static char* getFontSubset (const OString& aFontName)
4403 {
4404  OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8));
4406  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4407  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4408  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4409 
4410  boost::property_tree::ptree aTree;
4411  aTree.put("commandName", ".uno:FontSubset");
4412  boost::property_tree::ptree aValues;
4413 
4414  if ( pList && !aFoundFont.isEmpty() )
4415  {
4416  sal_uInt16 nFontCount = pList->GetFontNameCount();
4417  sal_uInt16 nItFont = 0;
4418  for (; nItFont < nFontCount; ++nItFont)
4419  {
4420  if (aFoundFont == pList->GetFontName(nItFont).GetFamilyName())
4421  {
4422  break;
4423  }
4424  }
4425 
4426  if ( nItFont < nFontCount )
4427  {
4428  FontCharMapRef xFontCharMap (new FontCharMap());
4429  auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
4430  const vcl::Font& aFont(pList->GetFontName(nItFont));
4431 
4432  aDevice->SetFont(aFont);
4433  aDevice->GetFontCharMap(xFontCharMap);
4434  SubsetMap aSubMap(xFontCharMap);
4435 
4436  for (auto const& subset : aSubMap.GetSubsetMap())
4437  {
4438  boost::property_tree::ptree aChild;
4439  aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin())));
4440  aValues.push_back(std::make_pair("", aChild));
4441  }
4442  }
4443  }
4444 
4445  aTree.add_child("commandValues", aValues);
4446  std::stringstream aStream;
4447  boost::property_tree::write_json(aStream, aTree);
4448  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4449  assert(pJson); // Don't handle OOM conditions
4450  strcpy(pJson, aStream.str().c_str());
4451  pJson[aStream.str().size()] = '\0';
4452  return pJson;
4453 }
4454 
4455 static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand)
4456 {
4457  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4458 
4459  boost::property_tree::ptree aTree;
4460  aTree.put("commandName", pCommand);
4461  uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4462  uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
4463  uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames();
4464 
4465  static const std::vector<OUString> aWriterStyles =
4466  {
4467  "Text body",
4468  "Quotations",
4469  "Title",
4470  "Subtitle",
4471  "Heading 1",
4472  "Heading 2",
4473  "Heading 3"
4474  };
4475 
4476  // We need to keep a list of the default style names
4477  // in order to filter these out later when processing
4478  // the full list of styles.
4479  std::set<OUString> aDefaultStyleNames;
4480 
4481  boost::property_tree::ptree aValues;
4482  for (sal_Int32 nStyleFam = 0; nStyleFam < aStyleFamilies.getLength(); ++nStyleFam)
4483  {
4484  boost::property_tree::ptree aChildren;
4485  OUString sStyleFam = aStyleFamilies[nStyleFam];
4486  uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY);
4487 
4488  // Writer provides a huge number of styles, we have a list of 7 "default" styles which
4489  // should be shown in the normal dropdown, which we should add to the start of the list
4490  // to simplify their selection.
4491  if (sStyleFam == "ParagraphStyles"
4492  && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT)
4493  {
4494  for (const OUString& rStyle: aWriterStyles)
4495  {
4496  aDefaultStyleNames.insert( rStyle );
4497 
4498  boost::property_tree::ptree aChild;
4499  aChild.put("", rStyle.toUtf8());
4500  aChildren.push_back(std::make_pair("", aChild));
4501  }
4502  }
4503 
4504  const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
4505  for (const OUString& rStyle: aStyles )
4506  {
4507  // Filter out the default styles - they are already at the top
4508  // of the list
4509  if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() ||
4510  (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) )
4511  {
4512  boost::property_tree::ptree aChild;
4513  aChild.put("", rStyle.toUtf8());
4514  aChildren.push_back(std::make_pair("", aChild));
4515  }
4516  }
4517  aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren);
4518  }
4519 
4520  // Header & Footer Styles
4521  {
4522  OUString sName;
4523  boost::property_tree::ptree aChild;
4524  boost::property_tree::ptree aChildren;
4525  const OUString sPageStyles("PageStyles");
4526  uno::Reference<beans::XPropertySet> xProperty;
4527  uno::Reference<container::XNameContainer> xContainer;
4528 
4529  if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer))
4530  {
4531  uno::Sequence<OUString> aSeqNames = xContainer->getElementNames();
4532  for (sal_Int32 itName = 0; itName < aSeqNames.getLength(); itName++)
4533  {
4534  bool bIsPhysical;
4535  sName = aSeqNames[itName];
4536  xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY);
4537  if (xProperty.is() && (xProperty->getPropertyValue("IsPhysical") >>= bIsPhysical) && bIsPhysical)
4538  {
4539  xProperty->getPropertyValue("DisplayName") >>= sName;
4540  aChild.put("", sName.toUtf8());
4541  aChildren.push_back(std::make_pair("", aChild));
4542  }
4543  }
4544  aValues.add_child("HeaderFooter", aChildren);
4545  }
4546  }
4547 
4548  {
4549  boost::property_tree::ptree aCommandList;
4550 
4551  {
4552  boost::property_tree::ptree aChild;
4553 
4554  OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM);
4555 
4556  boost::property_tree::ptree aName;
4557  aName.put("", sClearFormat.toUtf8());
4558  aChild.push_back(std::make_pair("text", aName));
4559 
4560  boost::property_tree::ptree aCommand;
4561  aCommand.put("", ".uno:ResetAttributes");
4562  aChild.push_back(std::make_pair("id", aCommand));
4563 
4564  aCommandList.push_back(std::make_pair("", aChild));
4565  }
4566 
4567  aValues.add_child("Commands", aCommandList);
4568  }
4569 
4570  aTree.add_child("commandValues", aValues);
4571  std::stringstream aStream;
4572  boost::property_tree::write_json(aStream, aTree);
4573  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4574  assert(pJson); // Don't handle OOM conditions
4575  strcpy(pJson, aStream.str().c_str());
4576  pJson[aStream.str().size()] = '\0';
4577  return pJson;
4578 }
4579 
4580 namespace {
4581 
4582 enum class UndoOrRedo
4583 {
4584  UNDO,
4585  REDO
4586 };
4587 
4588 }
4589 
4591 static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
4592 {
4593  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4594 
4595  auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
4596  if (!pBaseModel)
4597  return nullptr;
4598 
4599  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
4600  if (!pObjectShell)
4601  return nullptr;
4602 
4603  SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager();
4604  if (!pUndoManager)
4605  return nullptr;
4606 
4607  OUString aString;
4608  if (eCommand == UndoOrRedo::UNDO)
4609  aString = pUndoManager->GetUndoActionsInfo();
4610  else
4611  aString = pUndoManager->GetRedoActionsInfo();
4612  char* pJson = strdup(aString.toUtf8().getStr());
4613  return pJson;
4614 }
4615 
4617 static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
4618 {
4619  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4620 
4621  uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4622  std::stringstream aStream;
4623  // We want positions of the track changes also which is not possible from
4624  // UNO. Enable positioning information for text documents only for now, so
4625  // construct the tracked changes JSON from inside the sw/, not here using UNO
4626  if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
4627  {
4628  uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
4629  boost::property_tree::ptree aRedlines;
4630  for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
4631  {
4632  uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
4633  boost::property_tree::ptree aRedline;
4634  aRedline.put("index", nIndex);
4635 
4636  OUString sAuthor;
4637  xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
4638  aRedline.put("author", sAuthor.toUtf8().getStr());
4639 
4640  OUString sType;
4641  xRedline->getPropertyValue("RedlineType") >>= sType;
4642  aRedline.put("type", sType.toUtf8().getStr());
4643 
4644  OUString sComment;
4645  xRedline->getPropertyValue("RedlineComment") >>= sComment;
4646  aRedline.put("comment", sComment.toUtf8().getStr());
4647 
4648  OUString sDescription;
4649  xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
4650  aRedline.put("description", sDescription.toUtf8().getStr());
4651 
4652  util::DateTime aDateTime;
4653  xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
4654  OUString sDateTime = utl::toISO8601(aDateTime);
4655  aRedline.put("dateTime", sDateTime.toUtf8().getStr());
4656 
4657  aRedlines.push_back(std::make_pair("", aRedline));
4658  }
4659 
4660  boost::property_tree::ptree aTree;
4661  aTree.add_child("redlines", aRedlines);
4662  boost::property_tree::write_json(aStream, aTree);
4663  }
4664  else
4665  {
4666  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4667  if (!pDoc)
4668  {
4669  SetLastExceptionMsg("Document doesn't support tiled rendering");
4670  return nullptr;
4671  }
4672  OUString aTrackedChanges = pDoc->getTrackedChanges();
4673  aStream << aTrackedChanges.toUtf8();
4674  }
4675 
4676  char* pJson = strdup(aStream.str().c_str());
4677  return pJson;
4678 }
4679 
4680 
4682 static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
4683 {
4684  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4685  if (!pDoc)
4686  {
4687  SetLastExceptionMsg("Document doesn't support tiled rendering");
4688  return nullptr;
4689  }
4690  OUString aAuthors = pDoc->getTrackedChangeAuthors();
4691  return strdup(aAuthors.toUtf8().getStr());
4692 }
4693 
4694 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
4695 {
4696  comphelper::ProfileZone aZone("doc_getCommandValues");
4697 
4698  SolarMutexGuard aGuard;
4700 
4701  OString aCommand(pCommand);
4702  static const OString aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
4703  static const OString aCellCursor(".uno:CellCursor");
4704  static const OString aFontSubset(".uno:FontSubset&name=");
4705 
4706  if (!strcmp(pCommand, ".uno:LanguageStatus"))
4707  {
4708  return getLanguages(pCommand);
4709  }
4710  else if (!strcmp(pCommand, ".uno:CharFontName"))
4711  {
4712  return getFonts(pCommand);
4713  }
4714  else if (!strcmp(pCommand, ".uno:StyleApply"))
4715  {
4716  return getStyles(pThis, pCommand);
4717  }
4718  else if (aCommand == ".uno:Undo")
4719  {
4720  return getUndoOrRedo(pThis, UndoOrRedo::UNDO);
4721  }
4722  else if (aCommand == ".uno:Redo")
4723  {
4724  return getUndoOrRedo(pThis, UndoOrRedo::REDO);
4725  }
4726  else if (aCommand == ".uno:AcceptTrackedChanges")
4727  {
4728  return getTrackedChanges(pThis);
4729  }
4730  else if (aCommand == ".uno:TrackedChangeAuthors")
4731  {
4732  return getTrackedChangeAuthors(pThis);
4733  }
4734  else if (aCommand == ".uno:ViewAnnotations")
4735  {
4736  return getPostIts(pThis);
4737  }
4738  else if (aCommand == ".uno:ViewAnnotationsPosition")
4739  {
4740  return getPostItsPos(pThis);
4741  }
4742  else if (aCommand == ".uno:RulerState")
4743  {
4744  return getRulerState(pThis);
4745  }
4746  else if (aCommand.startsWith(aViewRowColumnHeaders))
4747  {
4748  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4749  if (!pDoc)
4750  {
4751  SetLastExceptionMsg("Document doesn't support tiled rendering");
4752  return nullptr;
4753  }
4754 
4755  tools::Rectangle aRectangle;
4756  if (aCommand.getLength() > aViewRowColumnHeaders.getLength())
4757  {
4758  // Command has parameters.
4759  int nX = 0;
4760  int nY = 0;
4761  int nWidth = 0;
4762  int nHeight = 0;
4763  OString aArguments = aCommand.copy(aViewRowColumnHeaders.getLength() + 1);
4764  sal_Int32 nParamIndex = 0;
4765  do
4766  {
4767  OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
4768  sal_Int32 nIndex = 0;
4769  OString aKey;
4770  OString aValue;
4771  do
4772  {
4773  OString aToken = aParamToken.getToken(0, '=', nIndex);
4774  if (!aKey.getLength())
4775  aKey = aToken;
4776  else
4777  aValue = aToken;
4778  }
4779  while (nIndex >= 0);
4780  if (aKey == "x")
4781  nX = aValue.toInt32();
4782  else if (aKey == "y")
4783  nY = aValue.toInt32();
4784  else if (aKey == "width")
4785  nWidth = aValue.toInt32();
4786  else if (aKey == "height")
4787  nHeight = aValue.toInt32();
4788  }
4789  while (nParamIndex >= 0);
4790 
4791  aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
4792  }
4793 
4794  OUString aHeaders = pDoc->getRowColumnHeaders(aRectangle);
4795  if (aHeaders.isEmpty())
4796  return nullptr;
4797  else
4798  return convertOUString(aHeaders);
4799  }
4800  else if (aCommand.startsWith(aCellCursor))
4801  {
4802  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4803  if (!pDoc)
4804  {
4805  SetLastExceptionMsg("Document doesn't support tiled rendering");
4806  return nullptr;
4807  }
4808  // Ignore command's deprecated parameters.
4809  return convertOString(pDoc->getCellCursor());
4810  }
4811  else if (aCommand.startsWith(aFontSubset))
4812  {
4813  return getFontSubset(OString(pCommand + aFontSubset.getLength()));
4814  }
4815  else
4816  {
4817  SetLastExceptionMsg("Unknown command, no values returned");
4818  return nullptr;
4819  }
4820 }
4821 
4822 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight,
4823  int nTileTwipWidth, int nTileTwipHeight)
4824 {
4825  comphelper::ProfileZone aZone("doc_setClientZoom");
4826 
4827  SolarMutexGuard aGuard;
4829 
4830  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4831  if (!pDoc)
4832  {
4833  SetLastExceptionMsg("Document doesn't support tiled rendering");
4834  return;
4835  }
4836 
4837  pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
4838 }
4839 
4840 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight)
4841 {
4842  comphelper::ProfileZone aZone("doc_setClientVisibleArea");
4843 
4844  SolarMutexGuard aGuard;
4846 
4847  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4848  if (!pDoc)
4849  {
4850  SetLastExceptionMsg("Document doesn't support tiled rendering");
4851  return;
4852  }
4853 
4854  tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
4855  pDoc->setClientVisibleArea(aRectangle);
4856 }
4857 
4858 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden)
4859 {
4860  comphelper::ProfileZone aZone("doc_setOutlineState");
4861 
4862  SolarMutexGuard aGuard;
4864 
4865  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4866  if (!pDoc)
4867  {
4868  SetLastExceptionMsg("Document doesn't support tiled rendering");
4869  return;
4870  }
4871 
4872  pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden);
4873 }
4874 
4875 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis,
4876  const char* pOptions)
4877 {
4878  comphelper::ProfileZone aZone("doc_createView");
4879 
4880  SolarMutexGuard aGuard;
4882 
4883  OUString aOptions = getUString(pOptions);
4884  const OUString aLanguage = extractParameter(aOptions, "Language");
4885 
4886  if (!aLanguage.isEmpty())
4887  {
4888  // Set the LOK language tag, used for dialog tunneling.
4891  }
4892 
4893  int nId = SfxLokHelper::createView();
4894 
4895 #ifdef IOS
4896  (void) pThis;
4897 #else
4898  forceSetClipboardForCurrentView(pThis);
4899 #endif
4900 
4901  return nId;
4902 }
4903 
4904 static int doc_createView(LibreOfficeKitDocument* pThis)
4905 {
4906  return doc_createViewWithOptions(pThis, nullptr); // No options.
4907 }
4908 
4909 static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
4910 {
4911  comphelper::ProfileZone aZone("doc_destroyView");
4912 
4913  SolarMutexGuard aGuard;
4915 
4917 
4919 }
4920 
4921 static void doc_setView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
4922 {
4923  comphelper::ProfileZone aZone("doc_setView");
4924 
4925  SolarMutexGuard aGuard;
4927 
4928  SfxLokHelper::setView(nId);
4929 }
4930 
4931 static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4932 {
4933  comphelper::ProfileZone aZone("doc_getView");
4934 
4935  SolarMutexGuard aGuard;
4937 
4938  return SfxLokHelper::getView();
4939 }
4940 
4941 static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
4942 {
4943  comphelper::ProfileZone aZone("doc_getViewsCount");
4944 
4945  SolarMutexGuard aGuard;
4947 
4948  return SfxLokHelper::getViewsCount();
4949 }
4950 
4951 static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int* pArray, size_t nSize)
4952 {
4953  comphelper::ProfileZone aZone("doc_getViewsIds");
4954 
4955  SolarMutexGuard aGuard;
4957 
4958  return SfxLokHelper::getViewIds(pArray, nSize);
4959 }
4960 
4961 static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language)
4962 {
4963  comphelper::ProfileZone aZone("doc_setViewLanguage");
4964 
4965  SolarMutexGuard aGuard;
4967 
4968  OUString sLanguage = OStringToOUString(language, RTL_TEXTENCODING_UTF8);
4969  SfxLokHelper::setViewLanguage(nId, sLanguage);
4970  SfxLokHelper::setViewLocale(nId, sLanguage);
4971 }
4972 
4973 
4974 
4975 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
4976  const char* pFontName,
4977  const char* pChar,
4978  int* pFontWidth,
4979  int* pFontHeight)
4980 {
4981  return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0);
4982 }
4983 
4984 unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/,
4985  const char* pFontName,
4986  const char* pChar,
4987  int* pFontWidth,
4988  int* pFontHeight,
4989  int pOrientation)
4990 {
4991  comphelper::ProfileZone aZone("doc_renderFont");
4992 
4993  SolarMutexGuard aGuard;
4995 
4996  OString aSearchedFontName(pFontName);
4997  OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8));
4999  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
5000  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
5001  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
5002 
5003  const int nDefaultFontSize = 25;
5004 
5005  if ( pList )
5006  {
5007  sal_uInt16 nFontCount = pList->GetFontNameCount();
5008  for (sal_uInt16 i = 0; i < nFontCount; ++i)
5009  {
5010  const FontMetric& rFontMetric = pList->GetFontName(i);
5011  const OUString& aFontName = rFontMetric.GetFamilyName();
5012  if (aSearchedFontName != aFontName.toUtf8())
5013  continue;
5014 
5015  if (aText.isEmpty())
5016  aText = rFontMetric.GetFamilyName();
5017 
5018  auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
5019  ::tools::Rectangle aRect;
5020  vcl::Font aFont(rFontMetric);
5021  aFont.SetFontSize(Size(0, nDefaultFontSize));
5022  aFont.SetOrientation(pOrientation);
5023  aDevice->SetFont(aFont);
5024  aDevice->GetTextBoundRect(aRect, aText);
5025  if (aRect.IsEmpty())
5026  break;
5027 
5028  int nFontWidth = aRect.BottomRight().X() + 1;
5029  int nFontHeight = aRect.BottomRight().Y() + 1;
5030 
5031  if (!(nFontWidth > 0 && nFontHeight > 0))
5032  break;
5033 
5034  if (*pFontWidth > 0 && *pFontHeight > 0)
5035  {
5036  double fScaleX = *pFontWidth / static_cast<double>(nFontWidth) / 1.5;
5037  double fScaleY = *pFontHeight / static_cast<double>(nFontHeight) / 1.5;
5038 
5039  double fScale = std::min(fScaleX, fScaleY);
5040 
5041  if (fScale >= 1.0)
5042  {
5043  int nFontSize = fScale * nDefaultFontSize;
5044  aFont.SetFontSize(Size(0, nFontSize));
5045  aDevice->SetFont(aFont);
5046  }
5047 
5048  aRect = tools::Rectangle(0, 0, *pFontWidth, *pFontHeight);
5049 
5050  nFontWidth = *pFontWidth;
5051  nFontHeight = *pFontHeight;
5052 
5053  }
5054 
5055  unsigned char* pBuffer = static_cast<unsigned char*>(malloc(4 * nFontWidth * nFontHeight));
5056  if (!pBuffer)
5057  break;
5058 
5059  memset(pBuffer, 0, nFontWidth * nFontHeight * 4);
5060  aDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
5061  aDevice->SetOutputSizePixelScaleOffsetAndBuffer(
5062  Size(nFontWidth, nFontHeight), Fraction(1.0), Point(),
5063  pBuffer);
5064 
5065  if (*pFontWidth > 0 && *pFontHeight > 0)
5066  {
5067  DrawTextFlags const nStyle =
5068  DrawTextFlags::Center
5069  | DrawTextFlags::VCenter
5070  | DrawTextFlags::Bottom
5071  | DrawTextFlags::MultiLine
5072  | DrawTextFlags::WordBreak;// | DrawTextFlags::WordBreakHyphenation ;
5073 
5074  aDevice->DrawText(aRect, aText, nStyle);
5075  }
5076  else
5077  {
5078  *pFontWidth = nFontWidth;
5079  *pFontHeight = nFontHeight;
5080 
5081  aDevice->DrawText(Point(0,0), aText);
5082  }
5083 
5084 
5085  return pBuffer;
5086  }
5087  }
5088  return nullptr;
5089 }
5090 
5091 
5092 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
5093  unsigned char* pBuffer,
5094  const int nX, const int nY,
5095  const int nWidth, const int nHeight)
5096 {
5097  doc_paintWindowDPI(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, 1.0);
5098 }
5099 
5100 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
5101  unsigned char* pBuffer,
5102  const int nX, const int nY,
5103  const int nWidth, const int nHeight,
5104  const double fDPIScale)
5105 {
5106  doc_paintWindowForView(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, fDPIScale, -1);
5107 }
5108 
5109 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
5110  unsigned char* pBuffer, const int nX, const int nY,
5111  const int nWidth, const int nHeight,
5112  const double fDPIScale, int viewId)
5113 {
5114  comphelper::ProfileZone aZone("doc_paintWindowDPI");
5115 
5116  SolarMutexGuard aGuard;
5118 
5119  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5120  if (!pWindow)
5121  {
5122  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5123  return;
5124  }
5125 
5126  // Used to avoid work in setView if set.
5128 
5129  if (viewId >= 0)
5130  doc_setView(pThis, viewId);
5131 
5132  // Setup cairo (or CoreGraphics, in the iOS case) to draw with the changed DPI scale (and return
5133  // back to 1.0 when the painting finishes)
5136 
5137 #if defined(IOS)
5138 
5139  CGContextRef cgc = CGBitmapContextCreate(pBuffer, nWidth, nHeight, 8, nWidth*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little);
5140 
5141  CGContextTranslateCTM(cgc, 0, nHeight);
5142  CGContextScaleCTM(cgc, fDPIScale, -fDPIScale);
5143 
5144  SystemGraphicsData aData;
5145  aData.rCGContext = cgc;
5146 
5147  ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
5148  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
5149 
5150  pDevice->SetOutputSizePixel(Size(nWidth, nHeight));
5151 
5152  MapMode aMapMode(pDevice->GetMapMode());
5153  aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
5154  pDevice->SetMapMode(aMapMode);
5155 
5156  pWindow->PaintToDevice(pDevice.get(), Point(0, 0), Size());
5157 
5158  CGContextRelease(cgc);
5159 
5160 #else
5161 
5162  ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::DEFAULT);
5163  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
5164 
5165  pDevice->SetOutputSizePixelScaleOffsetAndBuffer(Size(nWidth, nHeight), Fraction(1.0), Point(), pBuffer);
5166 
5167  MapMode aMapMode(pDevice->GetMapMode());
5168  aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale)));
5169  pDevice->SetMapMode(aMapMode);
5170 
5171  pWindow->PaintToDevice(pDevice.get(), Point(0, 0), Size());
5172 #endif
5173 
5175 }
5176 
5177 static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nAction, const char* pData)
5178 {
5179  comphelper::ProfileZone aZone("doc_postWindow");
5180 
5181  SolarMutexGuard aGuard;
5183 
5184  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5185  if (!pWindow)
5186  {
5187  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
5188  return;
5189  }
5190 
5191  if (nAction == LOK_WINDOW_CLOSE)
5192  {
5193  bool bWasDialog = vcl::CloseDialog(pWindow);
5194  if (!bWasDialog)
5195  {
5196  if (FloatingWindow* pFloatWin = dynamic_cast<FloatingWindow*>(pWindow.get()))
5197  pFloatWin->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
5198  }
5199  }
5200  else if (nAction == LOK_WINDOW_PASTE)
5201  {
5202  OUString aMimeType;
5203  css::uno::Sequence<sal_Int8> aData;
5204  std::vector<beans::PropertyValue> aArgs(jsonToPropertyValuesVector(pData));
5205  {
5206  aArgs.size() == 2 &&
5207  aArgs[0].Name == "MimeType" && (aArgs[0].Value >>= aMimeType) &&
5208  aArgs[1].Name == "Data" && (aArgs[1].Value >>= aData);
5209  }
5210 
5211  if (!aMimeType.isEmpty() && aData.hasElements())
5212  {
5213  uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(aMimeType, aData));
5214  uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard);
5215  xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
5216  pWindow->SetClipboard(xClipboard);
5217 
5218  KeyEvent aEvent(0, KEY_PASTE, 0);
5219  Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
5220  }
5221  else
5222  SetLastExceptionMsg("Window command 'paste': wrong parameters.");
5223  }
5224 }
5225 
5226 // CERTIFICATE AND DOCUMENT SIGNING
5227 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
5228  const unsigned char* pCertificateBinary, const int nCertificateBinarySize,
5229  const unsigned char* pPrivateKeyBinary, const int nPrivateKeySize)
5230 {
5231  comphelper::ProfileZone aZone("doc_insertCertificate");
5232 
5233  if (!xContext.is())
5234  return false;
5235 
5236  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5237 
5238  if (!pDocument->mxComponent.is())
5239  return false;
5240 
5241  SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
5242  if (!pBaseModel)
5243  return false;
5244 
5245  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
5246 
5247  if (!pObjectShell)
5248  return false;
5249 
5250  uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
5251  uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
5252  if (!xSecurityContext.is())
5253  return false;
5254 
5255  uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
5256  uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
5257 
5258  if (!xCertificateCreator.is())
5259  return false;
5260 
5261  uno::Sequence<sal_Int8> aCertificateSequence;
5262 
5263  std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
5264  std::string aCertificateBase64String = extractCertificate(aCertificateString);
5265  if (!aCertificateBase64String.empty())
5266  {
5267  OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
5268  comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
5269  }
5270  else
5271  {
5272  aCertificateSequence.realloc(nCertificateBinarySize);
5273  std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
5274  }
5275 
5276  uno::Sequence<sal_Int8> aPrivateKeySequence;
5277  std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeySize);
5278  std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
5279  if (!aPrivateKeyBase64String.empty())
5280  {
5281  OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
5282  comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
5283  }
5284  else
5285  {
5286  aPrivateKeySequence.realloc(nPrivateKeySize);
5287  std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeySize, aPrivateKeySequence.begin());
5288  }
5289 
5290  uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
5291 
5292  if (!xCertificate.is())
5293  return false;
5294 
5295  SolarMutexGuard aGuard;
5296 
5297  return pObjectShell->SignDocumentContentUsingCertificate(xCertificate);
5298 }
5299 
5300 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
5301  const unsigned char* pCertificateBinary, const int nCertificateBinarySize)
5302 {
5303  comphelper::ProfileZone aZone("doc_addCertificate");
5304 
5305  if (!xContext.is())
5306  return false;
5307 
5308  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5309 
5310  if (!pDocument->mxComponent.is())
5311  return false;
5312 
5313  SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
5314  if (!pBaseModel)
5315  return false;
5316 
5317  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
5318 
5319  if (!pObjectShell)
5320  return false;
5321 
5322  uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
5323  uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
5324  if (!xSecurityContext.is())
5325  return false;
5326 
5327  uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
5328  uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
5329 
5330  if (!xCertificateCreator.is())
5331  return false;
5332 
5333  uno::Sequence<sal_Int8> aCertificateSequence;
5334 
5335  std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
5336  std::string aCertificateBase64String = extractCertificate(aCertificateString);
5337  if (!aCertificateBase64String.empty())
5338  {
5339  OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
5340  comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
5341  }
5342  else
5343  {
5344  aCertificateSequence.realloc(nCertificateBinarySize);
5345  std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
5346  }
5347 
5348  uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, "TCu,Cu,Tu");
5349 
5350  if (!xCertificate.is())
5351  return false;
5352 
5353  SAL_INFO("lok", "Certificate Added = IssuerName: " << xCertificate->getIssuerName() << " SubjectName: " << xCertificate->getSubjectName());
5354 
5355  return true;
5356 }
5357 
5358 static int doc_getSignatureState(LibreOfficeKitDocument* pThis)
5359 {
5360  comphelper::ProfileZone aZone("doc_getSignatureState");
5361 
5362  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5363 
5364  if (!pDocument->mxComponent.is())
5365  return int(SignatureState::UNKNOWN);
5366 
5367  SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
5368  if (!pBaseModel)
5369  return int(SignatureState::UNKNOWN);
5370 
5371  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
5372  if (!pObjectShell)
5373  return int(SignatureState::UNKNOWN);
5374 
5375  SolarMutexGuard aGuard;
5376 
5377  pObjectShell->RecheckSignature(false);
5378 
5379  return int(pObjectShell->GetDocumentSignatureState());
5380 }
5381 
5382 static void doc_resizeWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId,
5383  const int nWidth, const int nHeight)
5384 {
5385  SolarMutexGuard aGuard;
5386  if (gImpl)
5387  gImpl->maLastExceptionMsg.clear();
5388 
5389  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
5390  if (!pWindow)
5391  {
5392  gImpl->maLastExceptionMsg = "Document doesn't support dialog resizing, or window not found.";
5393  return;
5394  }
5395 
5396  pWindow->SetSizePixel(Size(nWidth, nHeight));
5397 }
5398 
5399 static void doc_completeFunction(LibreOfficeKitDocument* pThis, int nIndex)
5400 {
5401  SolarMutexGuard aGuard;
5403 
5404  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5405  if (!pDoc)
5406  {
5407  SetLastExceptionMsg("Document doesn't support tiled rendering");
5408  return;
5409  }
5410 
5411  pDoc->completeFunction(nIndex);
5412 }
5413 
5414 static char* lo_getError (LibreOfficeKit *pThis)
5415 {
5416  comphelper::ProfileZone aZone("lo_getError");
5417 
5418  SolarMutexGuard aGuard;
5419 
5420  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5421  return convertOUString(pLib->maLastExceptionMsg);
5422 }
5423 
5424 static void lo_freeError(char* pFree)
5425 {
5426  free(pFree);
5427 }
5428 
5429 static char* lo_getFilterTypes(LibreOfficeKit* pThis)
5430 {
5431  SolarMutexGuard aGuard;
5433 
5434  LibLibreOffice_Impl* pImpl = static_cast<LibLibreOffice_Impl*>(pThis);
5435 
5436  if (!xSFactory.is())
5438 
5439  if (!xSFactory.is())
5440  {
5441  pImpl->maLastExceptionMsg = "Service factory is not available";
5442  return nullptr;
5443  }
5444 
5445  uno::Reference<container::XNameAccess> xTypeDetection(xSFactory->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY);
5446  const uno::Sequence<OUString> aTypes = xTypeDetection->getElementNames();
5447  boost::property_tree::ptree aTree;
5448  for (const OUString& rType : aTypes)
5449  {
5450  uno::Sequence<beans::PropertyValue> aValues;
5451  if (xTypeDetection->getByName(rType) >>= aValues)
5452  {
5453  auto it = std::find_if(aValues.begin(), aValues.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "MediaType"; });
5454  OUString aValue;
5455  if (it != aValues.end() && (it->Value >>= aValue) && !aValue.isEmpty())
5456  {
5457  boost::property_tree::ptree aChild;
5458  aChild.put("MediaType", aValue.toUtf8());
5459  aTree.add_child(rType.toUtf8().getStr(), aChild);
5460  }
5461  }
5462  }
5463  std::stringstream aStream;
5464  boost::property_tree::write_json(aStream, aTree);
5465  return strdup(aStream.str().c_str());
5466 }
5467 
5468 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long const features)
5469 {
5470  comphelper::ProfileZone aZone("lo_setOptionalFeatures");
5471 
5472  SolarMutexGuard aGuard;
5474 
5475  LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis);
5476  pLib->mOptionalFeatures = features;
5477  if (features & LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK)
5479  if (features & LOK_FEATURE_NO_TILED_ANNOTATIONS)
5481  if (features & LOK_FEATURE_RANGE_HEADERS)
5483  if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK)
5485 }
5486 
5487 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
5488  const char* pURL, const char* pPassword)
5489 {
5490  comphelper::ProfileZone aZone("lo_setDocumentPassword");