1 /* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include "DataFlavorMapping.hxx"
23 #include "HtmlFmtFlt.hxx"
24 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
25 #include <com/sun/star/datatransfer/XMimeContentType.hpp>
26 #include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
27 #include <com/sun/star/lang/IllegalArgumentException.hpp>
28 #include <com/sun/star/uno/Sequence.hxx>
29 #include <comphelper/processfactory.hxx>
31 #include <rtl/ustring.hxx>
32 #include <sal/log.hxx>
33 #include <osl/endian.h>
39 #include <UIKit/UIKit.h>
40 #include <MobileCoreServices/MobileCoreServices.h>
43 using namespace css::datatransfer
;
44 using namespace css::uno
;
45 using namespace css::lang
;
50 /* Determine whether or not a DataFlavor is valid.
52 bool isValidFlavor(const DataFlavor
& aFlavor
)
54 size_t len
= aFlavor
.MimeType
.getLength();
55 Type dtype
= aFlavor
.DataType
;
57 && ((dtype
== cppu::UnoType
<Sequence
<sal_Int8
>>::get())
58 || (dtype
== cppu::UnoType
<OUString
>::get())));
61 OUString
NSStringToOUString(const NSString
* cfString
)
63 assert(cfString
&& "Invalid parameter");
65 const char* utf8Str
= [cfString UTF8String
];
66 unsigned int len
= rtl_str_getLength(utf8Str
);
68 return OUString(utf8Str
, len
, RTL_TEXTENCODING_UTF8
);
71 NSString
* OUStringToNSString(const OUString
& ustring
)
73 OString utf8Str
= OUStringToOString(ustring
, RTL_TEXTENCODING_UTF8
);
74 return [NSString stringWithCString
:utf8Str
.getStr() encoding
:NSUTF8StringEncoding
];
77 NSString
* PBTYPE_UTF8PLAINTEXT
= (__bridge NSString
*)kUTTypeUTF8PlainText
;
78 NSString
* PBTYPE_RTF
= (__bridge NSString
*)kUTTypeRTF
;
79 NSString
* PBTYPE_PNG
= (__bridge NSString
*)kUTTypePNG
;
80 NSString
* PBTYPE_JPEG
= (__bridge NSString
*)kUTTypeJPEG
;
81 NSString
* PBTYPE_HTML
= (__bridge NSString
*)kUTTypeHTML
;
82 NSString
* PBTYPE_PDF
= (__bridge NSString
*)kUTTypePDF
;
84 const char* FLAVOR_SESX
85 = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
86 const char* FLAVOR_SLSDX
= "application/"
87 "x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link "
88 "Source Descriptor (XML)\"";
89 const char* FLAVOR_LSX
90 = "application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"";
91 const char* FLAVOR_EOX
92 = "application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"";
93 const char* FLAVOR_SVXB
94 = "application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"";
95 const char* FLAVOR_GDIMF
96 = "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
97 const char* FLAVOR_SODX
= "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star "
98 "Object Descriptor (XML)\"";
101 NSString
* SystemFlavor
;
102 const char* OOoFlavor
;
103 const char* HumanPresentableName
;
104 bool DataTypeOUString
; // sequence<byte> otherwise
107 // The SystemFlavor member is nil for the cases where there is no predefined pasteboard type UTI and
108 // we use the internal MIME type (media type) also on the pasteboard. That is OK, there dos not seem
109 // to be a requirement that the types are well-formed UTIs even on iOS. For an introduction to UTIs,
111 // https://alastairs-place.net/blog/2012/06/06/utis-are-better-than-you-think-and-heres-why/
113 // In those cases the MIME type might actually have parameters appended, separated by semicolons.
114 // At least the FLAVOR_SODX one must have at least a typename="%PRODUCTNAME %PRODUCTVERSION
115 // Spreadsheet" parameter (with macros expanded and translated) for LO to recognise it. See
116 // lcl_TestFormat() in sc/source/ui/view/cellsh.cxx.
118 static const FlavorMap flavorMap
[]
119 = { { PBTYPE_UTF8PLAINTEXT
, "text/plain;charset=utf-16", "Unicode Text (UTF-16)", true },
120 { PBTYPE_RTF
, "text/rtf", "Rich Text Format", false },
121 { PBTYPE_PNG
, "image/png", "Portable Network Graphics", false },
122 { PBTYPE_JPEG
, "image/jpeg", "JPEG", false },
123 { PBTYPE_HTML
, "text/html", "Plain HTML", false },
124 { PBTYPE_PDF
, "application/pdf", "PDF File", false },
125 { nil
, FLAVOR_SESX
, "Star Embed Source (XML)", false },
126 { nil
, FLAVOR_SLSDX
, "Star Link Source Descriptor (XML)", false },
127 { nil
, FLAVOR_LSX
, "Star Link Source (XML)", false },
128 { nil
, FLAVOR_EOX
, "Star Embedded Object (XML)", false },
129 { nil
, FLAVOR_SVXB
, "SVXB (StarView Bitmap/Animation", false },
130 { nil
, FLAVOR_GDIMF
, "GDIMetaFile", false },
131 { nil
, FLAVOR_SODX
, "Star Object Descriptor (XML)", false } };
133 #define SIZE_FLAVOR_MAP (sizeof(flavorMap) / sizeof(FlavorMap))
135 inline bool isByteSequenceType(const Type
& theType
)
137 return (theType
== cppu::UnoType
<Sequence
<sal_Int8
>>::get());
140 inline bool isOUStringType(const Type
& theType
)
142 return (theType
== cppu::UnoType
<OUString
>::get());
145 } // unnamed namespace
147 /* A base class for other data provider.
149 class DataProviderBaseImpl
: public DataProvider
152 DataProviderBaseImpl(const Any
& data
);
153 DataProviderBaseImpl(id data
);
154 virtual ~DataProviderBaseImpl() override
;
158 //NSData* mSystemData;
162 DataProviderBaseImpl::DataProviderBaseImpl(const Any
& data
)
168 DataProviderBaseImpl::DataProviderBaseImpl(id data
)
171 [mSystemData retain
];
174 DataProviderBaseImpl::~DataProviderBaseImpl()
178 [mSystemData release
];
182 class Utf8DataProvider
: public DataProviderBaseImpl
185 Utf8DataProvider(const Any
& data
);
186 Utf8DataProvider(NSData
* data
);
188 NSData
* getSystemData() override
;
189 Any
getOOoData() override
;
192 Utf8DataProvider::Utf8DataProvider(const Any
& data
)
193 : DataProviderBaseImpl(data
)
197 Utf8DataProvider::Utf8DataProvider(NSData
* data
)
198 : DataProviderBaseImpl(data
)
202 NSData
* Utf8DataProvider::getSystemData()
208 ustr
.convertToString(&strUtf8
, RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
210 return [NSData dataWithBytes
:strUtf8
.getStr() length
:strUtf8
.getLength()];
213 Any
Utf8DataProvider::getOOoData()
219 oOOData
<<= OUString(static_cast<const char*>([mSystemData bytes
]), [mSystemData length
],
220 RTL_TEXTENCODING_UTF8
);
230 class ByteSequenceDataProvider
: public DataProviderBaseImpl
233 ByteSequenceDataProvider(const Any
& data
);
234 ByteSequenceDataProvider(NSData
* data
);
236 NSData
* getSystemData() override
;
237 Any
getOOoData() override
;
240 ByteSequenceDataProvider::ByteSequenceDataProvider(const Any
& data
)
241 : DataProviderBaseImpl(data
)
245 ByteSequenceDataProvider::ByteSequenceDataProvider(NSData
* data
)
246 : DataProviderBaseImpl(data
)
250 NSData
* ByteSequenceDataProvider::getSystemData()
252 Sequence
<sal_Int8
> rawData
;
255 return [NSData dataWithBytes
:rawData
.getArray() length
:rawData
.getLength()];
258 Any
ByteSequenceDataProvider::getOOoData()
264 unsigned int flavorDataLength
= [mSystemData length
];
265 Sequence
<sal_Int8
> byteSequence
;
266 byteSequence
.realloc(flavorDataLength
);
267 memcpy(byteSequence
.getArray(), [mSystemData bytes
], flavorDataLength
);
268 oOOData
<<= byteSequence
;
278 class HTMLFormatDataProvider
: public DataProviderBaseImpl
281 HTMLFormatDataProvider(NSData
* data
);
283 NSData
* getSystemData() override
;
284 Any
getOOoData() override
;
287 HTMLFormatDataProvider::HTMLFormatDataProvider(NSData
* data
)
288 : DataProviderBaseImpl(data
)
292 NSData
* HTMLFormatDataProvider::getSystemData()
294 Sequence
<sal_Int8
> textHtmlData
;
295 mData
>>= textHtmlData
;
297 Sequence
<sal_Int8
> htmlFormatData
= TextHtmlToHTMLFormat(textHtmlData
);
299 return [NSData dataWithBytes
:htmlFormatData
.getArray() length
:htmlFormatData
.getLength()];
302 Any
HTMLFormatDataProvider::getOOoData()
308 unsigned int flavorDataLength
= [mSystemData length
];
309 Sequence
<sal_Int8
> unkHtmlData
;
311 unkHtmlData
.realloc(flavorDataLength
);
312 memcpy(unkHtmlData
.getArray(), [mSystemData bytes
], flavorDataLength
);
314 Sequence
<sal_Int8
>* pPlainHtml
= &unkHtmlData
;
315 Sequence
<sal_Int8
> plainHtml
;
317 if (isHTMLFormat(unkHtmlData
))
319 plainHtml
= HTMLFormatToTextHtml(unkHtmlData
);
320 pPlainHtml
= &plainHtml
;
323 oOOData
<<= *pPlainHtml
;
333 DataFlavorMapper::DataFlavorMapper()
335 Reference
<XComponentContext
> xContext
= comphelper::getProcessComponentContext();
336 mrXMimeCntFactory
= MimeContentTypeFactory::create(xContext
);
339 DataFlavorMapper::~DataFlavorMapper()
341 // release potential NSStrings
342 for (OfficeOnlyTypes::iterator it
= maOfficeOnlyTypes
.begin(); it
!= maOfficeOnlyTypes
.end();
345 [it
->second release
];
350 DataFlavor
DataFlavorMapper::systemToOpenOfficeFlavor(const NSString
* systemDataFlavor
) const
352 DataFlavor oOOFlavor
;
354 for (size_t i
= 0; i
< SIZE_FLAVOR_MAP
; i
++)
356 if ((flavorMap
[i
].SystemFlavor
== nil
357 && ([systemDataFlavor
358 isEqualToString
:[NSString stringWithUTF8String
:flavorMap
[i
].OOoFlavor
]]
360 [systemDataFlavor hasPrefix
:[[NSString stringWithUTF8String
:flavorMap
[i
].OOoFlavor
]
361 stringByAppendingString
:@
";"]]))
362 || (flavorMap
[i
].SystemFlavor
!= nil
&&
364 isEqualToString
:const_cast<NSString
*>(flavorMap
[i
].SystemFlavor
)]))
366 if (flavorMap
[i
].SystemFlavor
== nil
)
367 oOOFlavor
.MimeType
= NSStringToOUString(systemDataFlavor
);
369 oOOFlavor
.MimeType
= OUString::createFromAscii(flavorMap
[i
].OOoFlavor
);
370 oOOFlavor
.HumanPresentableName
371 = OUString::createFromAscii(flavorMap
[i
].HumanPresentableName
);
372 oOOFlavor
.DataType
= flavorMap
[i
].DataTypeOUString
373 ? cppu::UnoType
<OUString
>::get()
374 : cppu::UnoType
<Sequence
<sal_Int8
>>::get();
379 // look if this might be an internal type; if it comes in here it must have
380 // been through openOfficeToSystemFlavor before, so it should then be in the map
381 OUString
aTryFlavor(NSStringToOUString(systemDataFlavor
));
382 if (maOfficeOnlyTypes
.find(aTryFlavor
) != maOfficeOnlyTypes
.end())
384 oOOFlavor
.MimeType
= aTryFlavor
;
385 oOOFlavor
.HumanPresentableName
.clear();
386 oOOFlavor
.DataType
= cppu::UnoType
<Sequence
<sal_Int8
>>::get();
392 NSString
* DataFlavorMapper::openOfficeToSystemFlavor(const DataFlavor
& oOOFlavor
,
393 bool& rbInternal
) const
395 NSString
* sysFlavor
= nullptr;
398 for (size_t i
= 0; i
< SIZE_FLAVOR_MAP
; ++i
)
400 if (oOOFlavor
.MimeType
.startsWith(OUString::createFromAscii(flavorMap
[i
].OOoFlavor
)))
402 if (flavorMap
[i
].SystemFlavor
!= nil
)
403 sysFlavor
= flavorMap
[i
].SystemFlavor
;
405 sysFlavor
= OUStringToNSString(oOOFlavor
.MimeType
);
411 // For some reason, if we allow text/html, we get an OSL_ENSURE failure in xmloff that
412 // apparently is a symptom of something being seriously wrong:
413 // xmloff/source/transform/OOo2Oasis.cxx:1925: duplicate doc handler
414 // Because is then followed a bit later by an assertion failure:
415 // Assertion failed: (!m_pFirst && !m_pLast && "There are still indices registered"), function ~SwContentIndexReg, file [...]/sw/source/core/bastyp/index.cxx, line 226
417 if (oOOFlavor
.MimeType
== "text/html")
421 OfficeOnlyTypes::const_iterator it
= maOfficeOnlyTypes
.find(oOOFlavor
.MimeType
);
423 if (it
== maOfficeOnlyTypes
.end())
424 sysFlavor
= maOfficeOnlyTypes
[oOOFlavor
.MimeType
]
425 = OUStringToNSString(oOOFlavor
.MimeType
);
427 sysFlavor
= it
->second
;
433 NSString
* DataFlavorMapper::openOfficeImageToSystemFlavor()
435 if ([[UIPasteboard generalPasteboard
] containsPasteboardTypes
:@
[ PBTYPE_PNG
]])
437 else if ([[UIPasteboard generalPasteboard
] containsPasteboardTypes
:@
[ PBTYPE_JPEG
]])
439 else if ([[UIPasteboard generalPasteboard
] containsPasteboardTypes
:@
[ PBTYPE_PDF
]])
445 DataFlavorMapper::getDataProvider(const NSString
* systemFlavor
,
446 Reference
<XTransferable
> const& rTransferable
) const
448 DataProviderPtr_t dp
;
452 DataFlavor oOOFlavor
= systemToOpenOfficeFlavor(systemFlavor
);
454 Any data
= rTransferable
->getTransferData(oOOFlavor
);
456 if (isByteSequenceType(data
.getValueType()))
458 dp
= DataProviderPtr_t(new ByteSequenceDataProvider(data
));
460 else // Must be OUString type
462 SAL_WARN_IF(!isOUStringType(data
.getValueType()), "vcl", "must be OUString type");
463 dp
= DataProviderPtr_t(new Utf8DataProvider(data
));
466 catch (const UnsupportedFlavorException
& e
)
468 SAL_WARN("vcl.ios.clipboard",
469 "DataFlavorMapper::getDataProvider(): Exception: " << e
.Message
);
470 // Somebody violates the contract of the clipboard
471 // interface @see XTransferable
477 DataProviderPtr_t
DataFlavorMapper::getDataProvider(const NSString
* systemFlavor
,
480 DataProviderPtr_t dp
;
482 if (systemData
== nil
)
485 if ([systemFlavor caseInsensitiveCompare
:PBTYPE_UTF8PLAINTEXT
] == NSOrderedSame
)
487 dp
= DataProviderPtr_t(new Utf8DataProvider(systemData
));
489 else if ([systemFlavor caseInsensitiveCompare
:PBTYPE_HTML
] == NSOrderedSame
)
491 dp
= DataProviderPtr_t(new HTMLFormatDataProvider(systemData
));
495 dp
= DataProviderPtr_t(new ByteSequenceDataProvider(systemData
));
501 bool DataFlavorMapper::isValidMimeContentType(const OUString
& contentType
) const
507 Reference
<XMimeContentType
> xCntType(mrXMimeCntFactory
->createMimeContentType(contentType
));
509 catch (const IllegalArgumentException
& e
)
511 SAL_WARN("vcl.ios.clipboard",
512 "DataFlavorMapper::isValidMimeContentType(): Exception: " << e
.Message
);
519 NSArray
* DataFlavorMapper::flavorSequenceToTypesArray(
520 const css::uno::Sequence
<css::datatransfer::DataFlavor
>& flavors
) const
522 const sal_uInt32 nFlavors
= flavors
.getLength();
523 NSMutableArray
* array
= [[NSMutableArray alloc
] initWithCapacity
:1];
525 bool bNeedDummyInternalFlavor(false);
527 for (sal_uInt32 i
= 0; i
< nFlavors
; i
++)
529 if (flavors
[i
].MimeType
.startsWith("image/bmp"))
531 [array addObject
:PBTYPE_PNG
];
535 const NSString
* str
= openOfficeToSystemFlavor(flavors
[i
], bNeedDummyInternalFlavor
);
540 [array addObject
:str
];
545 return [array autorelease
];
548 css::uno::Sequence
<css::datatransfer::DataFlavor
>
549 DataFlavorMapper::typesArrayToFlavorSequence(NSArray
* types
) const
551 int nFormats
= [types count
];
552 Sequence
<DataFlavor
> flavors
;
554 for (int i
= 0; i
< nFormats
; i
++)
556 NSString
* sysFormat
= [types objectAtIndex
:i
];
557 DataFlavor oOOFlavor
= systemToOpenOfficeFlavor(sysFormat
);
559 if (isValidFlavor(oOOFlavor
))
561 flavors
.realloc(flavors
.getLength() + 1);
562 flavors
.getArray()[flavors
.getLength() - 1] = oOOFlavor
;
563 SAL_INFO("vcl.ios.clipboard",
564 "Mapped " << [sysFormat UTF8String
] << " to " << oOOFlavor
.MimeType
);
568 SAL_INFO("vcl.ios.clipboard",
569 "Was not able to map " << [sysFormat UTF8String
] << " to an internal flavour");
576 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */