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