I'm working on Xmarin Forms(PCL) project, I want to convert the StackLayout to Image / buffer and send it to printer for hard print.
Can anyone suggest how to do it in (Xamarin.Android & Xamarin.iOS).
You can't. Xamarin does not have that kind of feature. You should write a Renderer for your UIComponent.
Fortunately there is an Objective-C iOS implementation, and an Android one as well. You can inspire from them.
Taken from this link, which I have personally used, quite a while back though, the following code will take a screenshot of the entire page.
I ended up modifying the code to only take a screenshot of a specific view on the page and also changed a few other things but this example is what I based it off of, so let me know if you would rather see that code and/or if something below is not working for you.
First you create an interface in your Forms project, IScreenshotManager.cs for example:
public interface IScreenshotManager {
Task<byte[]> CaptureAsync();
}
Now we need to implement our interface in Android, ScreenshotManager.cs for example:
public class ScreenshotManager : IScreenshotManager {
public static Activity Activity { get; set; }
public async System.Threading.Tasks.Task<byte[]> CaptureAsync() {
if(Activity == null) {
throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
}
var view = Activity.Window.DecorView;
view.DrawingCacheEnabled = true;
Bitmap bitmap = view.GetDrawingCache(true);
byte[] bitmapData;
using (var stream = new MemoryStream()) {
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
}
Then set ScreenshotManager.Activity in MainActivity:
public class MainActivity : Xamarin.Forms.Platform.Android.FormsApplicationActivity {
protected override async void OnCreate(Android.OS.Bundle bundle) {
...
ScreenshotManager.Activity = this; //There are better ways to do this but this is what the example from the link suggests
...
}
}
Finally we implement this on iOS, ScreenshotManager.cs:
public class ScreenshotManager : IScreenshotManager {
public async System.Threading.Tasks.Task<byte[]> CaptureAsync() {
var view = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
UIGraphics.BeginImageContext(view.Frame.Size);
view.DrawViewHierarchy(view.Frame, true);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
using(var imageData = image.AsPNG()) {
var bytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
return bytes;
}
}
}
Related
As the title states, I need to use a TypefaceSpan object with a custom font present inside the Assets but I can't find the correct way to achieve this.
The file of the font is "HelveticaNeueLTCom-BdCn.ttf"
These are my two attempt which did not work for me:
// first attempt
var textViewTitle = new TextView(Context);
var span = new SpannableString("MyLongTitle");
span.SetSpan(new TypefaceSpan("HelveticaNeueLTCom-BdCn.ttf"), 0, 5, SpanTypes.ExclusiveExclusive);
textViewTitle.TextFormatted = span;
// second attempt
var textViewTitle = new TextView(Context);
var span = new SpannableString("MyLongTitle");
span.SetSpan(new Typeface(Typeface.CreateFromAsset(Context.Assets, "fonts/HelveticaNeueLTCom-BdCn.ttf")), 0, 5, SpanTypes.ExclusiveExclusive);
textViewTitle.TextFormatted = span;
Anyone has some hints or advice?
Almost one year later but I faced the same problem and I found this way of achieving it.
I saw you posted the same question in the Xamarin forum here but the link you wrote in your answer is broken. However, I think that it is this post that helped you as it helped me.
So here the way to go.
First create a custom TypefaceSpan like this
using System;
using Android.OS;
using Android.Runtime;
using Android.Text.Style;
using Android.Graphics;
using Android.Text;
namespace Droid.Spans
{
public class CustomTypefaceSpan : TypefaceSpan
{
private readonly Typeface _typeface;
public CustomTypefaceSpan(Typeface typeface)
: base(string.Empty)
{
_typeface = typeface;
}
public CustomTypefaceSpan(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public CustomTypefaceSpan(Parcel src)
: base(src)
{
}
public CustomTypefaceSpan(string family)
: base(family)
{
}
public override void UpdateDrawState(TextPaint ds)
{
ApplyTypeface(ds, _typeface);
}
public override void UpdateMeasureState(TextPaint paint)
{
ApplyTypeface(paint, _typeface);
}
private static void ApplyTypeface(Paint paint, Typeface tf)
{
paint.SetTypeface(tf);
}
}
}
Then use this CustomTypefaceSpan to add a span to the SpannableString
var spannableString = new SpannableString("Anything to write with a special font");
spannableString.SetSpan(new CustomTypefaceSpan(Typeface.CreateFromAsset(Assets, "HelveticaNeueLTCom-BdCn.ttf"), 0, 7, SpanTypes.InclusiveInclusive);
textView.TextFormatted = spannableString;
I'm trying to create an app to read QR codes using Monotouch and C# port of Zxing but I'm hitting memory issues. While the app processes captured screen frames the app receives memory warnings and is then shut down. I have removed the call to Zxing to track down where the memory issue stems from and can reproduce the issue with just capturing the screen image in a loop.
Here is the code:
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Threading;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using MonoTouch.CoreGraphics;
using com.google.zxing;
using com.google.zxing.common;
using System.Collections;
using MonoTouch.AudioToolbox;
using iOS_Client.Utilities;
namespace iOS_Client.Controllers
{
public class CameraOverLayView : UIView
{
private Thread _thread;
private CameraViewController _parentViewController;
private Hashtable hints;
private static com.google.zxing.MultiFormatReader _multiFormatReader = null;
private static RectangleF picFrame = new RectangleF(0, 146, 320, 157);
private static UIImage _theScreenImage = null;
public CameraOverLayView(CameraViewController parentController) : base()
{
Initialize();
_parentViewController = parentController;
}
private void Initialize()
{
}
private bool Worker()
{
Result resultb = null;
if(DeviceHardware.Version == DeviceHardware.HardwareVersion.iPhone4
|| DeviceHardware.Version == DeviceHardware.HardwareVersion.iPhone4S)
{
picFrame = new RectangleF(0, 146*2, 320*2, 157*2);
}
if(hints==null)
{
var list = new ArrayList();
list.Add (com.google.zxing.BarcodeFormat.QR_CODE);
hints = new Hashtable();
hints.Add(com.google.zxing.DecodeHintType.POSSIBLE_FORMATS, list);
hints.Add (com.google.zxing.DecodeHintType.TRY_HARDER, true);
}
if(_multiFormatReader == null)
{
_multiFormatReader = new com.google.zxing.MultiFormatReader();
}
using (var screenImage = CGImage.ScreenImage.WithImageInRect(picFrame))
{
using (_theScreenImage = UIImage.FromImage(screenImage))
{
Bitmap srcbitmap = new System.Drawing.Bitmap(_theScreenImage);
LuminanceSource source = null;
BinaryBitmap bitmap = null;
try {
source = new RGBLuminanceSource(srcbitmap, screenImage.Width, screenImage.Height);
bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
_multiFormatReader.Hints = hints;
resultb = null;
//_multiFormatReader.decodeWithState(bitmap);
if(resultb != null && resultb.Text!=null)
{
InvokeOnMainThread( () => _parentViewController.BarCodeScanned(resultb));
}
}
catch (ReaderException re)
{
//continue;
}
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
finally {
if(bitmap!=null)
bitmap = null;
if(source!=null)
source = null;
if(srcbitmap!=null)
{
srcbitmap.Dispose();
srcbitmap = null;
}
}
}
}
return resultb != null;
}
public void StartWorker()
{
if(_thread==null)
{
_thread = new Thread(()=> {
bool result = false;
while (result == false)
{
result = Worker();
Thread.Sleep (67);
}
});
}
_thread.Start();
}
public void StopWorker()
{
if(_thread!=null)
{
_thread.Abort();
_thread = null;
}
//Just in case
_multiFormatReader = null;
hints = null;
}
protected override void Dispose(bool disposing)
{
StopWorker();
base.Dispose(disposing);
}
}
}
Interestingly I took a look at http://blog.reinforce-lab.com/2010/02/monotouchvideocapturinghowto.html to try and see how others were capturing and processing video and this code suffers from the same as mine, quitting after about 40 seconds with memory warnings.
Hopefully the QR codes will be scanned in less than 40 seconds but I'm not sure if the memory ever gets released so the problem may crop up after many codes have been scanned. Either way it should be possible to capture a video feed continuously without memory issues right?
This is somewhat counter-intuitive, but the ScreenImage property will create a new CGImage instance every time you call it, so you must call Dispose on that object as well:
using (var img = CGImage.ScreenImage) {
using (var screenImage = img.WithImageInRect(picFrame))
{
}
}
I will just add the actual solution that worked for me which combined information from previous answers. The code inside the loop looks like:
using (var pool = new NSAutoreleasePool ())
{
using (var img = CGImage.ScreenImage)
{
using (var screenImage = img.WithImageInRect(picFrame))
{
using (_theScreenImage = UIImage.FromImage(screenImage))
{
}
}
}
}
GC.Collect();
The original System.Drawing.Bitmap from zxing.MonoTouch suffered from a lack of Dispose which made it never release the unmanaged memory it allocated.
The more recent one (from your link) does free the unmanaged memory when Dispose is called (it's better). However it creates a bitmap context (in it's constructor) and does not dispose it manually (e.g. with a using). So it relies on the garbage collector (GC) to do it later...
In many cases this is not a big issue since the GC will, eventually, free this context instance and will reclaim the associated memory. However if you're doing this in a loop it's possible you'll run out of (unmanaged) memory before the GC kicks in. That will get you memory warnings and iOS can decide to kill your application (or it could crash by itself).
but I'm not sure if the memory ever gets released
Yes, it should be - but maybe not as fast as you need the memory back. Implementing (and using) IDisposable correctly will solve this.
Either way it should be possible to capture a video feed continuously without memory issues right?
Yes. Make sure you're releasing your memory as soon as possible, e.g. with using (var ...) { }, and ensure the 3rd party code you use does the same.
Does anyone know of a MonoMac sample that shows how to implement Print (to a printer)? I haven't been able to find one.
I don't know of one, but the conceptual docs from Apple are relevant, and their sample snippets should be straightforward to port to C#: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Printing/Printing.html
I created a PrintDocument class a bit like this:
(You'd need to set a proper size and add some drawing in drawrect)
public class PrintDocument:NSView {
NSPrintOperation MyPrinter = null;
static IntPtr selCurrentContext = Selector.GetHandle ("currentContext");
static IntPtr classNSGraphicsContext = Class.GetHandle ("NSGraphicsContext");
public PrintDocument ()
{
MyPrinter=NSPrintOperation.FromView(this);
this.SetFrameSize(new SizeF(600,800));
}
public void Print ()
{
MyPrinter.RunOperation()
}
public override void DrawRect (RectangleF dirtyRect)
{
var context = new NSGraphicsContext (Messaging.IntPtr_objc_msgSend (classNSGraphicsContext, selCurrentContext));
//NSPrintOperation.CurrentOperation
}
}
I am trying to display a PNG image on blackberry device for OS 5.0 using J2ME MIDlet class instead of a blackberry RIMlet class. Can I use J2ME MIDlet instead of RIMlets? Would it be compatible with blackberry as blackberry do support J2ME? Can I get the image from it?
To display an image on the screen of a BlackBerry® device, create an Image object and populate it by calling the static Image.createImage() method. Provide the location of the image as a parameter.
refer display an PNG image using J2ME MIDlet classes on blackberry device
Can i use J2ME MIDlet instead of RIMlets...
YES, but there are certain advantages like mentioned here.
and if you want to go with MIDlet, here is an example using ImageItem,
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class ImageItemMIDlet extends MIDlet implements CommandListener{
private Command exit;
private ImageItem imageItem;
private Image image;
private Display display;
private Form form;
public ImageItemMIDlet(){
try{
image = Image.createImage("/yourImage.png");
} catch (Exception e){ }
imageItem = new ImageItem("This is the IMAGE_ITEM Application",
image, ImageItem.LAYOUT_DEFAULT, "image");
}
public void startApp(){
form = new Form("ImageItem Example");
display = Display.getDisplay(this);
exit = new Command("Exit", Command.EXIT, 1);
form.append(imageItem);
form.addCommand(exit);
form.setCommandListener(this);
display.setCurrent(form);
}
public void pauseApp(){}
public void destroyApp(boolean unconditional){
notifyDestroyed();
}
public void commandAction(Command c, Displayable d){
String label = c.getLabel();
if(label.equals("Exit")){
destroyApp(true);
}
}
}
public class Midlet extends MIDlet {
public Display display;
public void startApp() {
Canvas obj = new DrawImage();
display = Display.getDisplay(this);
display.setCurrent(obj);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public class DrawImage extends Canvas{
int width = getWidth();
int height = getHeight();
protected void paint(Graphics g) {
try {
System.out.println("111111");
Image image = Image.createImage("/Waterfall.png");
if(image != null)
g.drawImage(image, 0, 0, Graphics.TOP | Graphics.LEFT);
else
System.out.println("2222");
} catch (IOException ex) {
System.out.println(ex);
}
}
}
}
Its good to use Midlet with canvas to show on canvas because if you use Midlet with Form then its show image but its also showing the theme of mobile in background of form. If you use canvas you can use also background image for your front image.
Thanks
I have a MVC application, which creates a Chart in the business logic like this:
StatisticsModel.Chart.Width = 150
StatisticsModel.Chart.Height = 300
StatisticsModel.Chart.Attributes.Add("align", "left")
StatisticsModel.Chart.Titles.Add("Statistics for: " + StatisticsModel.ProductNumbers)
StatisticsModel.Chart.ChartAreas.Add(New ChartArea)
StatisticsModel.Chart.Series.Add(New Series)
StatisticsModel.Chart.Series(0).ChartType = SeriesChartType.Column
StatisticsModel.Chart.Series(0).Points.DataBindXY(StatisticsModel.FailedTPDescriptionList, "key", StatisticsModel.FailedTPDescriptionList, "value")
Now, I am trying to implement it in the View, but I have read many articles, and they suggest me to put the chart in a different controller. But that would mean I have to send the Chart object there, as I have many functions, that require a chart, and I thought the easiest way is to implement it in the Model, and then rendering it from there.
I tried using: http://code-inside.de/blog-in/2008/11/27/howto-use-the-new-aspnet-chart-controls-with-aspnet-mvc/
But the:
#Code
Dim writer As New HtmlTextWriter(Page.Response.Output)
End Code
Didn't work for me. I am using VB.NET
Can anyone help me? Suggestions are very welcome.
There are many, many ways of creating and showing charts in MVC, and the link you referred to is pretty good IMHO. I'm using c#, but the way I'm doing it is to use an img-tag in the view and point the src-attribute to a Controller action:
<img id="diagram" src="<%=Url.Action("DrawChartImage", "Home") %>" alt="Chart Diagram" />
The controller action returns a FileContentResult:
public ActionResult DrawChartImage()
{
using (var chartHelper = new ChartHelper())
{
//get data
var data = GetSomeDataForTheChart();
//draw chart
chartHelper.Draw(data);
//return chart as png image
return File(chartHelper.Image, "image/png");
}
}
The ChartHelper class implements IDisposable and has a helper property (Image) which returns the chart as a file, NOTE this is just sample/snippet code to show what I mean:
public class ChartHelper : IDisposable
{
private readonly Chart _chart;
public Chart Chart
{
get
{
return _chart;
}
}
public byte[] Image
{
get
{
using (var ms = new MemoryStream())
{
_chart.SaveImage(ms);
return ms.GetBuffer();
}
}
}
public ChartHelper()
{
_chart = new Chart();
_chart.Height = 300;
_chart.Width = 800;
_chart.ImageType = ChartImageType.Png;
_chart.Titles.Add("some title");
_chart.Legends.Add("some legend");
_chart.ChartAreas.Add("some chart area");
}
public void Draw(List<Data> data)
{
var dataArrays = GetDataArrays(data); //another helper method...
var series = new Series(tag);
series.Name = tag;
series.Legend = "tags";
series.ChartType = SeriesChartType.Spline;
series.BorderWidth = 4;
//sample way to add data below...
series.Points.DataBindXY(dataArrays.Item1, dataArrays.Item2);
_chart.Series.Add(series);
}
public void Dispose()
{
_chart.Dispose();
}
}
Works pretty well for me, hope it helps even if it's in C#.
EDIT If you want to create the image/chart in business logic called from your "main" Controller action, maybe you can do something like this where you generate the image/chart and then save it to disk, cache or database and pick it up from the image rendering controller action:
public ActionResult Index()
{
//this is call to your business logic or similar which generates the chart
byte[] image = GenerateImage();
//save image to cache, disk or from database
HttpContext.Cache["image"] = image;
return View();
}
public ActionResult Image()
{
//get image from cache, disk or from database
var image = HttpContext.Cache["image"] as byte[];
return File(image, "image/png");
}
//some sample/dummy code to generate image from a template
private byte[] GenerateImage()
{
using (var ms = new MemoryStream())
{
using (var image = System.Drawing.Image.FromFile(#"c:/temp/template.png"))
using (var brush = new SolidBrush(System.Drawing.Color.Black))
using (var bmp = new System.Drawing.Bitmap(image, image.Width, image.Height))
using (var g = System.Drawing.Graphics.FromImage(bmp))
{
g.DrawString(DateTime.Now.ToLongTimeString(), new Font("Consolas", 10), brush, 10, 10);
bmp.Save(ms, ImageFormat.Png);
}
return ms.ToArray();
}
}
And the view would be:
<img src="#Url.Action("Image")"/>