private static DataPackageView GetFromNative(NSPasteboard pasteboard) { if (pasteboard is null) { throw new ArgumentException(nameof(pasteboard)); } var dataPackage = new DataPackage(); // Extract all the standard data format information from the pasteboard items. // Each format can only be used once; therefore, generally the last occurrence of the format will be the one used. foreach (NSPasteboardItem item in pasteboard.PasteboardItems) { if (item.Types.Contains(NSPasteboard.NSPasteboardTypeTIFF) || item.Types.Contains(NSPasteboard.NSPasteboardTypePNG)) { // Images may be very large, we never want to load them until they are needed. // Therefore, create a data provider used to asynchronously fetch the image. dataPackage.SetDataProvider( StandardDataFormats.Bitmap, async cancellationToken => { NSImage?image = null; /* Some apps, including Photos, don't appear to put image data in the pasteboard. * Instead, the image URL is provided although the type indicates it is an image. * * To get around this an image is read as follows: * (1) If the pasteboard contains an image type then: * (2) Attempt to read the image as an object (NSImage). * This may fail as some tested apps provide a URL although declare an image (Photos). * With other apps (such as web browsers) an image will be read correctly here. * (3) If reading as an NSImage object fails, attempt to read the image from a file URL (local images) * (4) If reading from a file URL fails, attempt to read the image from a URL (remote images) * * Reading as an NSImage object follows the docs here: * https://docs.microsoft.com/en-us/xamarin/mac/app-fundamentals/copy-paste#add-an-nsdocument */ var classArray = new Class[] { new Class("NSImage") }; if (pasteboard.CanReadObjectForClasses(classArray, null)) { NSObject[] objects = pasteboard.ReadObjectsForClasses(classArray, null); if (objects.Length > 0) { // Only use the first image found image = objects[0] as NSImage; } } // In order to get here the pasteboard must have declared it had image types. // However, if image is null, no objects were found and the image is likely a URL instead. if (image == null && item.Types.Contains(NSPasteboard.NSPasteboardTypeFileUrl)) { var url = item.GetStringForType(NSPasteboard.NSPasteboardTypeFileUrl); image = new NSImage(new NSUrl(url)); } if (image == null && item.Types.Contains(NSPasteboard.NSPasteboardTypeUrl)) { var url = item.GetStringForType(NSPasteboard.NSPasteboardTypeUrl); image = new NSImage(new NSUrl(url)); } if (image != null) { // Thanks to: https://stackoverflow.com/questions/13305028/monomac-best-way-to-convert-bitmap-to-nsimage/13355747 using (var imageData = image.AsTiff()) { var imgRep = NSBitmapImageRep.ImageRepFromData(imageData !) as NSBitmapImageRep; var data = imgRep !.RepresentationUsingTypeProperties(NSBitmapImageFileType.Png, null); return(new RandomAccessStreamReference(async ct => { return data.AsStream().AsRandomAccessStream().TrySetContentType("image/png"); })); } } else { // Return an empty image return(new RandomAccessStreamReference(async ct => { var stream = new MemoryStream(); stream.Position = 0; return stream.AsRandomAccessStream().TrySetContentType("image/png"); })); } }); } if (item.Types.Contains(NSPasteboard.NSPasteboardTypeHTML)) { var html = item.GetStringForType(NSPasteboard.NSPasteboardTypeHTML); if (html != null) { dataPackage.SetHtmlFormat(html); } } if (item.Types.Contains(NSPasteboard.NSPasteboardTypeRTF)) { var rtf = item.GetStringForType(NSPasteboard.NSPasteboardTypeRTF); if (rtf != null) { dataPackage.SetRtf(rtf); } } if (item.Types.Contains(NSPasteboard.NSPasteboardTypeFileUrl)) { // Drag and drop will use temporary URLs similar to: file:///.file/id=1234567.1234567 var tempFileUrl = item.GetStringForType(NSPasteboard.NSPasteboardTypeFileUrl); // Files may be very large, we never want to load them until they are needed. // Therefore, create a data provider used to asynchronously fetch the file. dataPackage.SetDataProvider( StandardDataFormats.StorageItems, async cancellationToken => { // Convert from a temp Url (see above example) into an absolute file path var fileUrl = new NSUrl(tempFileUrl); var file = await StorageFile.GetFileFromPathAsync(fileUrl.FilePathUrl.AbsoluteString); var storageItems = new List <IStorageItem>(); storageItems.Add(file); return(storageItems.AsReadOnly()); }); } if (item.Types.Contains(NSPasteboard.NSPasteboardTypeString)) { var text = item.GetStringForType(NSPasteboard.NSPasteboardTypeString); if (text != null) { dataPackage.SetText(text); } } if (item.Types.Contains(NSPasteboard.NSPasteboardTypeUrl)) { var url = item.GetStringForType(NSPasteboard.NSPasteboardTypeUrl); if (url != null) { DataPackage.SeparateUri( url, out string?webLink, out string?applicationLink); if (webLink != null) { dataPackage.SetWebLink(new Uri(webLink)); } if (applicationLink != null) { dataPackage.SetApplicationLink(new Uri(applicationLink)); } // Deprecated but still added for compatibility dataPackage.SetUri(new Uri(url)); } } } return(dataPackage.GetView()); }
public static DataPackageView GetContent() { var dataPackage = new DataPackage(); var manager = ContextHelper.Current.GetSystemService(Context.ClipboardService) as ClipboardManager; if (manager is null) { return(dataPackage.GetView()); } var clipData = manager.PrimaryClip; Uri /* ? */ clipApplicationLink = null; string /* ? */ clipHtml = null; string /* ? */ clipText = null; Uri /* ? */ clipUri = null; Uri /* ? */ clipWebLink = null; // Extract all the standard data format information from the clipboard. // Each format can only be used once; therefore, the last occurrence of the format will be the one used. if (clipData != null) { for (int itemIndex = 0; itemIndex < clipData.ItemCount; itemIndex++) { var item = clipData.GetItemAt(itemIndex); if (item != null) { var itemText = item.Text; if (itemText != null) { clipText = itemText; } var itemUriStr = item.Uri?.ToString(); if (itemUriStr != null) { DataPackage.SeparateUri( itemUriStr, out string webLink, out string applicationLink); clipWebLink = webLink != null ? new Uri(webLink) : null; clipApplicationLink = applicationLink != null ? new Uri(applicationLink) : null; clipUri = new Uri(itemUriStr); // Deprecated but still added for compatibility } var itemHtml = item.HtmlText; if (itemHtml != null) { clipHtml = itemHtml; } } } } // Add standard data formats to the data package. // This can be done synchronously on Android since the data is already available from above. if (clipApplicationLink != null) { dataPackage.SetApplicationLink(clipApplicationLink); } if (clipHtml != null) { dataPackage.SetHtmlFormat(clipHtml); } if (clipText != null) { dataPackage.SetText(clipText); } if (clipUri != null) { dataPackage.SetUri(clipUri); } if (clipWebLink != null) { dataPackage.SetWebLink(clipWebLink); } return(dataPackage.GetView()); }