Xamarin.Mac - How to highlight the selected text in a PDF file - macos

I'm actualy trying to add a markup annotation in a PDF with PDFKit, in Xamarin.Mac, so for OS X.
So my goal is to highlight permanently, as an annotation, a selected text in the PDF File, and save it to retrieve it when I open the file later.
The thing is, I can get the current selection and store it into a variable :
PdfSelection currentSelection = m_aPdfView.CurrentSelection;
And I can create an object PdfAnnotationMarkup :
//Create the markup annotation
var annot = new PdfAnnotationMarkup();
//add characteristics to the annotation
annot.Contents = currentSelectionText;
annot.MarkupType = PdfMarkupType.Highlight;
annot.Color = NSColor.Yellow;
annot.ShouldDisplay = true;
But I can't find, even though I checked a lot of different documentations, how to link the two of them.
There is no method giving the location of the currentSelection, or any hint to go in that direction.
Would anyone know of a way to make this possible ?
PS: I found that the subclasses of PDFAnnotation are deprecated on the Apple Developer Website , but not on the Xamarin Website, is there any way of knowing if both of them are related of entirely different ?
Thanks in advance for your help
EDIT : Here is the code I got and works perfectly. Thanks to svn's answers
//Get the current selection on the PDF file opened in the PdfView
PdfSelection currentSelection = m_aPdfView.CurrentSelection;
//Check if there is an actual selection right now
if (currentSelection != null)
{
currentSelection.GetBoundsForPage(currentSelection.Pages[0]);
//Create the markup annotation
var annot = new PdfAnnotationMarkup();
//add characteristics to the annotation
annot.Contents = "Test";
annot.MarkupType = PdfMarkupType.Highlight;
annot.Color = NSColor.Yellow;
annot.ShouldDisplay = true;
annot.ShouldPrint = true;
annot.UserName = "MyName";
//getting the current page
PdfPage currentPage = currentSelection.Pages[0];
//getting the bounds from the current selection and adding it to the annotation
var locationRect = currentSelection.GetBoundsForPage(currentPage);
getValuLabel.StringValue = locationRect.ToString();
//converting the CGRect object into CGPoints
CoreGraphics.CGPoint upperLeft = locationRect.Location;
CoreGraphics.CGPoint lowerLeft = new CoreGraphics.CGPoint(locationRect.X, (locationRect.Y + locationRect.Height));
CoreGraphics.CGPoint upperRight = new CoreGraphics.CGPoint((locationRect.X + locationRect.Width), locationRect.Y);
CoreGraphics.CGPoint lowerRight = new CoreGraphics.CGPoint((locationRect.X + locationRect.Width), (locationRect.Y + locationRect.Height));
//adding the CGPoints to a NSMutableArray
NSMutableArray pointsArray = new NSMutableArray();
pointsArray.Add(NSValue.FromCGPoint(lowerLeft));
pointsArray.Add(NSValue.FromCGPoint(lowerRight));
pointsArray.Add(NSValue.FromCGPoint(upperLeft));
pointsArray.Add(NSValue.FromCGPoint(upperRight));
//setting the quadrilateralPoints
annot.WeakQuadrilateralPoints = pointsArray;
//add the annotation to the PDF file current page
currentPage.AddAnnotation(annot);
//Tell the PdfView to update the display
m_aPdfView.NeedsDisplay = true;

A selection can span multiple pages. To get the location of a selection use:
var locationRect = currentSelection.GetBoundsForPage(currentSelection.Pages[0]);
You should be able to instantiate PdfAnnotationMarkup with bounds but the xamarin implementation does not expose that constuctor. But is does expose a bounds property
var annot = new PdfAnnotationMarkup();
annot.bounds = locationRect;
I have not tested this.

Related

MVVMCross Manual Bind in MvxFragment with MvxViewPagerFragmentAdapter

I'm using an implementation of Cheesebaron's MvxViewPagerFragmentAdapter example that can be found here http://blog.ostebaronen.dk/2013/07/fragments-and-viewpager-with-mvx.html
var fragments = new List<MvxViewPagerFragmentAdapter.FragmentInfo>
{
new MvxViewPagerFragmentAdapter.FragmentInfo
{
FragmentType = typeof(JobDetailsView),
Title = "Detail",
ViewModel = ViewModel
},
new MvxViewPagerFragmentAdapter.FragmentInfo
{
FragmentType = typeof(JobFeaturesView),
Title = "Info",
ViewModel = ViewModel
}
}
Within my JobDetailsView's OnCreateView, I can inflate my layout specified using BindingInflate and any bindings that I have specified in the XML layout work correctly.
I now have a requirement to bind some elements programmatically, which i have tried to do using CreateBindingSet, but the bindings do not work. I've tried a simple text property and a button click. Both of these work when specified in the XML.
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Android.OS.Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
this.EnsureBindingContextIsSet(savedInstanceState);
_view = this.BindingInflate(Resource.Layout.jobview_details, null);//jobview_withtabs_details
var signatureCard = _view.FindViewById<CardFeature>(Resource.Id.signature_cardview);
signatureCard.FindViewById<TextView>(Resource.Id.tv_basecard_header_title).Text = "Signature";
var signatureButton = signatureCard.FindViewById<Button>(Resource.Id.btn_basecard_footer).Text = "Capture";
var set = this.CreateBindingSet<JobDetailsView, JobWithTabsViewModel>();
set.Bind(signatureButton).To(vm => vm.SignatureClickCommand);
set.Apply();
return _view;
}
in my output window, i can see this, but i don't know what to do to fix it:
04-16 17:40:43.721 I/mono-stdout(27727): MvxBind:Error: 23.79 Empty binding target passed to MvxTargetBindingFactoryRegistry
MvxBind:Warning: 23.80 Failed to create target binding for binding for SignatureClickCommand
[0:] MvxBind:Warning: 23.80 Failed to create target binding for binding for SignatureClickCommand
04-16 17:40:43.731 I/mono-stdout(27727): MvxBind:Warning: 23.80 Failed to create target binding for binding for SignatureClickCommand
Anyone have any ideas?
UPDATE
In typical fashion, left it for an hour and realised my mistake.
I was being lazy by typing:
var signatureButton = signatureCard.FindViewById<Button>(Resource.Id.btn_basecard_footer).Text = "Capture"
Which was confusing the binding. Switched to:
var signatureButton = signatureCard.FindViewById<Button>(Resource.Id.btn_basecard_footer);
signatureButton.Text = "Capture";
And it works perfectly
An updated example using TabView and ViewPager is available here: https://github.com/MvvmCross/MvvmCross-AndroidSupport/tree/master/Samples
It uses the latest stuff from the Android design libraries.

Mailchimp.net API and content struct for editable content not displaying

I'm trying to send content to a campaign template in MailChimp using MailChimp.net API. My template contains `
<b>mc:edit="question"</b>`
And the MailChimp API wants a JSON string in a struct/associative array. However, I can't seem to get the data to show up in the editable section and I've tried several different ways:
CampaignSegmentOptions segmentOptions = new CampaignSegmentOptions();
segmentOptions.Match = "All";
CampaignCreateContent content = new CampaignCreateContent();
content.HTML = "<p>Testing</p>";
CampaignCreateOptions options = new CampaignCreateOptions();
options.Title = "Testing";
options.ListId = listID;
options.ToName = "Test Name";
options.FromEmail = "user#example.com";
options.FromName = "Testing from Example.com";
options.Subject = "Test Subject";
options.FacebookComments = false;
//Using struct attempt
sections s = new sections();
s.question = "What did the lion need?";
//Using string attempt
//String[] s = new string [] {"question\":\"What did the lion need?"};
//Using dictionary attempt
Dictionary<string,string> dict = new Dictionary<string,string>();
dict.Add("question","What did the lion need?");
content.Sections = dict;
Campaign result = mc.CreateCampaign("regular", options, content);
Any suggestions? Thank you in advance.
Solved. In my case the Dictionary approach was the only one that worked. I found that whenever I edited an existing MailChimp template, MailChimp appeared to rewrete my mc:edit fields after I saved them and added their own code making my mc:edit fields break. However, if I exported one of their templates to my desktop, and then imported it back as a new template, I found that editing the template on their website no longer changed my code after saving. I remember seeing a note in the documentation about exporting and importing, but nothing that implied that was the only way to keep my fields intact.

Windows Phone, set image source on async callback doesn't work?

I am getting images from Bing to display in my app. I followed Bing's instructions, I successfully retrieve the image's URLs, but for some reason, the emulator won't display them! Here's what I have
var bingContainer = new Bing.BingSearchContainer(new Uri("https://api.datamarket.azure.com/Bing/Search/"));
var accountKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
var imageQuery = bingContainer.Image("porsche", null, null, null, null, null, "Size:Medium");
imageQuery.BeginExecute(new AsyncCallback(this.ImageResultLoadedCallback), imageQuery);
Then, I get my images and try to set them here:
var imageQuery = (DataServiceQuery<Bing.ImageResult>)ar.AsyncState;
var enumerableImages = imageQuery.EndExecute(ar);
var imagesList = enumerableImages.ToList();
List<String> imList = new List<String>();
while (imList.Count != 3)
{
Bing.ImageResult tr = imagesList.First<Bing.ImageResult>();
if (tr.ContentType == "image/jpeg")
{
imList.Add(tr.MediaUrl);
}
imagesList.RemoveAt(0);
}
image1.Source = new BitmapImage(new Uri(#imList[0]));
image2.Source = new BitmapImage(new Uri(#imList[1]));
image3.Source = new BitmapImage(new Uri(#imList[2]));
When I debug, the process seems to just stop on those three last lines where I set the source.
Alright, after two days of frustration, I found out you cannot access the UI thread from an async callback. VS wasn't giving any errors, yet the images were not showing. An async callback runs alongside the main UI thread, so it can't access or change the elements in the UI. The easy workaround just involves wrapping the lines of code that access the UI like this:
Dispatcher.BeginInvoke(() =>
{
image1.Source = new BitmapImage(new Uri(#imList[0]));
image2.Source = new BitmapImage(new Uri(#imList[1]));
image3.Source = new BitmapImage(new Uri(#imList[2]));
});
It works now!
Are you sure MediaUrl is returning proper urls to images? What if you would use some hardcoded urls to images in the imList list, would the images load properly in image1, image2 and image3? The point i am getting to is that perhaps the quality of the data is incorrect. That is, though your query executes well, MediaURL is not containing a properly formatted URL.
Also, what is the exception you get when the debugger stops?

Unable to set firefox bookmark description for in Add-On Kit API

I found the nsINavBookmarksService, however since Firefox 3, it does not seem to have any API methods for getting/setting the Bookmark description. (API doc)
I've seen other Add-Ons modify the description as a method of synchronized data storage (which is exactly what I'm trying to do). I'm guessing perhaps the description is a non-gecko standard, and that's why it is not directly supported, but then there must be a completely different interface for manipulating Bookmarks that I haven't discovered.
Can anyone help with this newbie problem?
Starting with Firefox 3 the bookmarks have been merged into the Places database containing all of your browsing history. So to get the bookmarks you do a history query, like this (first line is specific to the Add-on SDK which you appear to be using):
var {Cc, Ci} = require("chrome");
var historyService = Cc["#mozilla.org/browser/nav-history-service;1"]
.getService(Ci.nsINavHistoryService);
var options = historyService.getNewQueryOptions();
var query = historyService.getNewQuery();
query.onlyBookmarked = true;
var result = historyService.executeQuery(query, options);
result.root.containerOpen = true;
for (var i = 0; i < result.root.childCount; i++)
{
var node = result.root.getChild(i);
console.log(node.title);
}
That's mostly identical to the code example here. Your problem is of course that nsINavHistoryResultNode has no way of storing data like a description. You can set annotations however, see Using the Places annotation service. So if you already have a node variable for your bookmark:
var annotationName = "my.extension.example.com/bookmarkDescription";
var ioService = Cc["#mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var uri = ioService.newURI(node.uri, null, null);
var annotationService = Cc["#mozilla.org/browser/annotation-service;1"]
.getService(Ci.nsIAnnotationService);
annotationService.setPageAnnotation(uri, annotationName,
"Some description", 0, Ci.nsIAnnotationService.EXPIRE_NEVER);
For reference: nsIAnnotationService.setPageAnnotation()

Dynamically added Raddocks Optimization?

We have a optimization problem regarding rad dock controls. The requirement of project is such that we are creating dynamic raddocks on the fly and adding it to a raddockzone, then we are saving the raddock "type" etc in a mssql db. We also have a collector window/raddockzone in which we have build a functionality where we can drag a dock and save it in collector. As with the first raddockzone we are adding the dock in collector on the fly. Now while adding a dock or moving it to another raddockzones it takes sometime. Our client is comparing it with the example of the demo link : http://demos.telerik.com/aspnet-ajax/dock/examples/content/defaultcs.aspx
Following is our code snippet to add dock on the fly:
private RadDockNew CreateRadDock()
{
//string[] allowedZones = { "RDZCollector", "RadDockZone2" };
int width = Convert.ToInt32((hdnWidth.Value == "") ? "520" : hdnWidth.Value);
RadDockNew dock = new RadDockNew();
dock.DockMode = DockMode.Docked;
dock.UniqueName = Guid.NewGuid().ToString().Replace("-", "a");
dock.ID = string.Format("RadDock{0}", dock.UniqueName);
//dock.Title = dock.UniqueName.Substring(dock.UniqueName.Length - 3);
dock.Width = Unit.Pixel(width);
dock.CssClass = "RadDockZoneMain";
//dock.AllowedZones = allowedZones;
dock.Style.Add("min-height", "290px");
dock.OnClientDockPositionChanged = "DropInCollector";
//dock.EnableViewState = false;
DockCommand cmd = new DockCommand();
cmd.Name = "Setting";
cmd.Text = "Setting";
cmd.OnClientCommand = "showSettings";
dock.Commands.Add(cmd);
DockCommand dc = new DockCommand();
dc.Text = "Trash";
dc.Name = "Trash";
dc.OnClientCommand = "CloseDock";
dc.CssClass = "rdClose";
dc.AutoPostBack = true;
dock.Commands.Add(dc);
DockToggleCommand cmd2 = new DockToggleCommand();
cmd2.CssClass = "rdCollapse";
cmd2.AlternateCssClass = "rdexpand";
cmd2.OnClientCommand = "ChangeImage";
//DockCommand collapse = new DockCommand();
//collapse.Text = "Collapse/Expand";
//collapse.Name = "Collapse/Expand";
//collapse.OnClientCommand = "CollapseDock";
//collapse.CssClass = "rdCollapse";
dock.Commands.Add(cmd2);
return dock;
}
Please tell if there is any way to optimize / make it faster.
Thanks.
I examined the attached code sample and I think that it is correct. My suggestions are to check if the problem persists if you use other storage medium, instead of a database or if there are client script errors on the page, containing the RadDocks.
As the setup of your project seems to be similar to the one, implemented in the My Portal demo, I would recommend using the example in the Code Library article Saving State of Dynamically Created RadDocks in DataBase using Hidden UpdatePanel as a reference.

Resources