I'm trying to access elements of an SVG file using WebKitGtk but I'm failing miserably. The program is in Vala, it's very simple and is just an attempt at getting data from a couple of elements:
using Gtk;
using WebKit;
public class WebKitTest : Window {
private WebView web_view;
private const string uri = "file:///tmp/test.svg";
public WebKitTest () {
set_default_size (800, 600);
web_view = new WebView ();
var scrolled_window = new ScrolledWindow (null, null);
scrolled_window.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
scrolled_window.add (this.web_view);
var vbox = new VBox (false, 0);
vbox.add (scrolled_window);
add (vbox);
this.destroy.connect (Gtk.main_quit);
show_all ();
this.web_view.load_uri (WebKitTest.uri);
var dom = web_view.get_dom_document ();
var el = dom.get_document_element ();
var child = el.first_element_child;
stdout.printf ("%s\n", child.tag_name);
var list = dom.get_elements_by_tag_name ("svg");
stdout.printf ("%lu\n", list.length);
assert (list == null);
var length = list.length;
assert (length == 0);
var svg = list.item (0);
var res = dom.evaluate ("//path", svg, null, 0, null);
DOMNode node;
while ((node = res.iterate_next ()) != null) {
stdout.printf ("%s\n", (node as DOMElement).tag_name);
}
}
public static void main (string[] args) {
Gtk.init (ref args);
var test = new WebKitTest ();
Gtk.main ();
return 0;
}
}
This compiles with valac --pkg webkitgtk-3.0 --pkg gtk+-3.0 webkittest.vala but if you've only got Gtk3 installed like I have you unfortunately need to do copy the webkit-1.0 .vapi and .deps files to new webkitgtk-3.0 ones and replace the occurrences of gdk-2.0 and gtk-2.0 in the .deps file to be gdk-3.0 and gtk-3.0 respectively.
I would think that using the DOM to access sub elements of an SVG would be simple, but as soon as this code gets to the print statement for child.tag_name it gives "HEAD" and nowhere in the file is that found. The file I'm loading should be pretty standard seeing as I used inkscape to make it.
Does WebKit do something funny that I'm not seeing to documents that it loads?
Thanks.
Update:
It definitely seems that WebKit loads everything into an HTML document because when I use GObject to retrieve the Type and print it for the node returned by get_elements_by_tag_name("body") I get WebKitDOMHTMLBodyElement. Be that as it may I tried to do the following XPath query on the DOMDocument
var nsres = dom.create_ns_resolver (body);
DOMXPathResult res = null;
try {
res = dom.evaluate ("//*", body, nsres, 0, null);
} catch (Error e) {
stderr.printf ("%s\n", e.message);
}
DOMNode node;
try {
while ((node = res.iterate_next ()) != null) {
stdout.printf ("%s\n", (node as DOMElement).tag_name);
}
} catch (Error e) {
stderr.printf ("%s\n", e.message);
}
and all I get for output is
HTML
HEAD
BODY
I'm lost now. The SVG file clearly loads correctly but it isn't part of the document?
I believe Webkit has a quirk where, if it doesn't know the MIME type of a document, it will wrap it in <html> tags. So I suspect the document you are trying to access has been transformed by Webkit into something like:
<html>
<head />
<body>
<svg>...etc...</svg>
</body>
</html>
That's why you are seeing a <head> tag in your code.
As a suggestion, try using Vala's equivalent of getElementsByTagName() to find your <svg> tag. From a quick look at the docs, it should be something like:
var dom = web_view.get_dom_document();
var el = dom.get_elements_by_tag_name("svg").item(0);
var child = var child = el.first_element_child;
Alternatively, if there is a way to tell WebKit/WebFrame that the MIME type of the file is "image/svg+xml", then that should also solve your problem.
Related
I'm a Vala/Gtk newbie and I'm trying to change the font size of a Gtk.Label, but I can't find a good way to do it.
I find out that I can use the markup like this :
var welcome_message = new Gtk.Label ("<span size='17000'>Hello</span>");
welcome_message.set_use_markup (true);
But it seems a little hackish.
What is the right way to do it ?
Thanks lethalman and nemequ.
I think it might help someone so here is a little example of how to use css with Vala.
using Gtk;
public class StyleApp1 : Gtk.Window
{
public StyleApp1()
{
this.title = "Style app example";
this.set_border_width (10);
this.set_position (Gtk.WindowPosition.CENTER);
this.set_default_size (350, 200);
this.destroy.connect (Gtk.main_quit);
var screen = this.get_screen ();
var css_provider = new Gtk.CssProvider();
string path = "styleapp1.css";
// test if the css file exist
if (FileUtils.test (path, FileTest.EXISTS))
{
try {
css_provider.load_from_path(path);
Gtk.StyleContext.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER);
} catch (Error e) {
error ("Cannot load CSS stylesheet: %s", e.message);
}
}
var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 10);
this.add (box);
var label = new Gtk.Label ("Thank you");
box.add (label);
var label2 = new Gtk.Label ("Stackoverflow");
label2.get_style_context().add_class("my_class");
box.add (label2);
}
}
static int main(string[] args) {
Gtk.init(ref args);
StyleApp1 win = new StyleApp1();
win.show_all();
Gtk.main();
return 0;
}
and the styleapp1.css file :
GtkWindow {
font-size: 17px;
}
.my_class {
color: pink;
}
NB : if you use add_provider instead of add_provider_for_screen. You have to use add_provider for every widget that you want to customize.
You could try with css, I think lately this is the preferred way. Give your label a class, then load a css. If you are going to change the font size of a label, I bet you are also going to customize other things so the css may be useful for you.
I tried this:
<awe:WebControl x:Name="webBrowser" Cursor="None" Source="http://example.com/"/>
but the cursor still shows.
I figured that I could alter the CSS of the page by adding the following line:
*{
cursor: none;
}
But, is there a solution for when I don't have the access to the actual page that I'm showing?
You can use a ResouceInterceptor and manipulate the page on the fly to insert custom CSS.
EDIT:
The following implementation should do the job. (It assumes there is a text.css file)
class ManipulatingResourceInterceptor : IResourceInterceptor
{
public ResourceResponse OnRequest(ResourceRequest request)
{
Stream stream = null;
//do stream manipulation
if (request.Url.ToString() == "http://your.web.url/test.css")
{
WebRequest myRequest;
myRequest = WebRequest.Create(request.Url);
Stream webStream = myRequest.GetResponse().GetResponseStream();
StreamReader webStreamReader = new StreamReader(webStream);
string webStreamContent = webStreamReader.ReadToEnd();
stream = webStream;
string extraContent = "*{cursor: none;}";
webStreamContent += extraContent;
byte[] responseBuffer = Encoding.UTF8.GetBytes(webStreamContent);
// Initialize unmanaged memory to hold the array.
int responseSize = Marshal.SizeOf(responseBuffer[0]) * responseBuffer.Length;
IntPtr pointer = Marshal.AllocHGlobal(responseSize);
try
{
// Copy the array to unmanaged memory.
Marshal.Copy(responseBuffer, 0, pointer, responseBuffer.Length);
return ResourceResponse.Create((uint)responseBuffer.Length, pointer, "text/css");
}
finally
{
// Data is not owned by the ResourceResponse. A copy is made
// of the supplied buffer. We can safely free the unmanaged memory.
Marshal.FreeHGlobal(pointer);
stream.Close();
}
}
return null;
}
public bool OnFilterNavigation(NavigationRequest request)
{
return false;
}
}
I've seen variations of my questions on stack overflow but haven't had any answers that have worked for me. I'm trying to convert an image I retrieve via UIImagePickerController to an NSData object. In the debugger after I call AsJPEG the NSData object has text that appears as...
System.Exception: Could not initialize an instance of the type 'MonoTouch.Foundation.NSString': the native 'initWithDa…
(note the debugger cuts off the string)
My code is fairly straight forward (taken form samples on stack overflow)
protected void Handle_FinishedPickingMedia (object sender, UIImagePickerMediaPickedEventArgs e) {
// determine what was selected, video or image
bool isImage = false;
switch(e.Info[UIImagePickerController.MediaType].ToString()) {
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
case "public.video":
Console.WriteLine("Video selected");
break;
}
string path = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
path = Path.Combine (path, "media");
if (!Directory.Exists (path))
Directory.CreateDirectory (path);
path = Path.Combine (path, Path.GetRandomFileName ());
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:"+referenceURL.ToString ());
// if it was an image, get the other image info
if(isImage) {
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if(originalImage != null) {
// do something with the image
Console.WriteLine ("got the original image");
using (NSData imageData = originalImage.AsJPEG(0.75f)) {
byte[] dataBytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, dataBytes, 0, Convert.ToInt32(imageData.Length));
File.WriteAllBytes (path, dataBytes);
}
}
} else { // if it's a video
// get video url
NSUrl mediaURL = e.Info[UIImagePickerController.MediaURL] as NSUrl;
if(mediaURL != null) {
// ...
}
}
// dismiss the picker
NavigationController.DismissViewController (true, null);
}
I've seen other posts that suggested it was the size of the UIImage, so I've experimented with cropping it. Same result. I've also tried AsPNG, same result. I even tried scaling down the image to 1/4 it's original size and still get the error.
I think the key is the mention of NSString, which tells me something's fishy... as the native C call used in Xcode doesn't involve an NSString, so I think something else is going on.
Any suggestions?
As noted in the comment from therealjohn, this appears to be an error with the debugger when it converts a value to a string to display it in the debugger window it seems to run into an error. The NSData object is actually fine.
I have the following in a Section:
_favElement = new StyledStringElement (string.Empty);
_favElement.Alignment = UITextAlignment.Center;
if (_room.IsFavourite) {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
} else {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
}
_favElement.Tapped += favElement_Tapped;
Then when I press the element I want the following to happen:
private void favElement_Tapped ()
{
if (_room.IsFavourite) {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
} else {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
}
_room.IsFavourite = !_room.IsFavourite;
}
However the image and text does not change in the actual element when the element is tapped. Is there a refresh method or something that must be called? I've also tried changing the Accessory on Tapped as well and nothing changes. The properties behind do reflect the correct values though.
An alternative to reloading the UITableView is to reload the Element using code like this (copied from Touch.Unit):
if (GetContainerTableView () != null) {
var root = GetImmediateRootElement ();
root.Reload (this, UITableViewRowAnimation.Fade);
}
assuming that your code is in DialogViewController,add this
this.ReloadData();
but in your case I recommend you to use BooleanImageElement
I'm trying to get a thumbnail image for a video that I've just chosen (or recorded) via the UIImagePickerController. In my picker's FinishedPickingMedia method, I get what I believe to be the path to the video file and use it to save the recording to album. The newly recorded video does indeed appear in my album. Now to get the thumbnail…
I've tried using the ALAssetsLibrary in a couple ways. First, I've enumerated all the videos and tried to find a match from the asset's UtiToUrlDictionary.Values[0] value, which reveals its file path. No entries matched the path I obtained from the FinishedPickingMedia method's mediaUrlKey.
My second attempt was to use the ALAssetsLibrary's AssetForUrl() method. Again, I tried my FinishedPickingMedia method's mediaUrlKey, but there was no match.
The UIImagePickerController -> FinishedPickingMedia -> mediaUrlKey returns a path of:
file://localhost/private/var/mobile/Applications/73037738-C060-4DE6-A1CA-698E6BE083F2/tmp//trim.iRBwWw.MOV
After painful digging and inspecting every video in my library, it the same video's path from the ALAssetsLibrary is:
assets-library://asset/asset.MOV?id=B073FCF6-83ED-436B-AFD8-42D0A6C70FC6&ext=MOV
Why are the video paths different between UIIMagePickerController and ALAssetsLibrary? How can I get ALAssetsLibrary to return the same video picked from UIImagePickerController?
[code for the picker]
public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{
var mediaUrlKey = new NSString("UIImagePickerControllerMediaURL");
var mediaPath = (NSUrl) info.ObjectForKey(mediaUrlKey);
if( recordView.RecordVideo )
{
if(mediaPath != null)
{
if(UIVideo.IsCompatibleWithSavedPhotosAlbum(mediaPath.Path))
{
UIVideo.SaveToPhotosAlbum(mediaPath.Path, SaveToPhotosAlbumResults);
}
else
{
using (var alert = new UIAlertView("Problem Encountered",
"Unable to save this recording.", null, "Ok!", null))
{
alert.Show();
}
}
}
}
else
{
recordView.VideoFileLocation = mediaPath;
//ALAssetsLibrary library = new ALAssetsLibrary();
//library.AssetForUrl( mediaPath, GetAssetResult, GetAssetError );
}
recordView.VideoFileLocation = mediaPath;
Console.WriteLine("***" + recordView.VideoFileLocation.ToString()+ "***");
recordView.ShowUploadButton();
recordView.SetThumbnailImage();
picker.DismissModalViewControllerAnimated(true);
}
Thank you
I think may be it is too late but the following code work as of now
public static UIImage GenerateImage(NSUrl videoUrl)
{
var asset = AVAsset.FromUrl(videoUrl);
var imageGenerator = AVAssetImageGenerator.FromAsset(asset);
imageGenerator.AppliesPreferredTrackTransform = true;
var actualTime = asset.Duration;
CoreMedia.CMTime cmTime = new CoreMedia.CMTime(1, 60);
NSError error;
var imageRef = imageGenerator.CopyCGImageAtTime(cmTime, out actualTime, out error);
if (imageRef == null)
return null;
var image = UIImage.FromImage(imageRef);
return image;
}
I think the section Can I Get To The Original File Representing This Image on the Disk in this question can help you.
This is how you can get a thumbnail within the picker delegate:
NSUrl data = new NSUrl(info.ObjectForKey(new NSString("UIImagePickerControllerMediaURL")).ToString());
//get the video thumbnail
MPMoviePlayerController movie = new MPMoviePlayerController(data);
movie.Stop();
UIImage videoThumbnail = movie.ThumbnailImageAt(0.0,MPMovieTimeOption.NearestKeyFrame);
movie.Stop();