I want to convert a pdf file to an image UI control in UWP using c#, xaml.
I've read another way to use the Flip Viewer, but I need each image file of the converted PDF file.
so I modified a bit of the existing sample To open a pdf file.
And my problem is that the quality of the converted image file is extremely bad.
I can not even see the letters.
but this quality problem is same on other pdf sample.
Take a look at my code.
private PdfDocument pdfDocument;
private async void LoadDocument()
{
pdfDocument = null;
//Output means my image UI control (pdf image will be added)
Output.Source = null;
//PageNumberBox shows current page
PageNumberBox.Text = "1";
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".pdf");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
try
{
pdfDocument = await PdfDocument.LoadFromFileAsync(file);
}
catch (Exception ex)
{
}
if (pdfDocument != null)
{ // I use this text to move page.
PageCountText.Text = pdfDocument.PageCount.ToString();
}
}
uint pageNumber;
if (!uint.TryParse(PageNumberBox.Text, out pageNumber) || (pageNumber < 1) || (pageNumber > pdfDocument.PageCount))
{
return;
}
Output.Source = null;
// Convert from 1-based page number to 0-based page index.
uint pageIndex = pageNumber-1 ;
using (PdfPage page = pdfDocument.GetPage(pageIndex))
{
var stream = new InMemoryRandomAccessStream();
await page.RenderToStreamAsync(stream);
BitmapImage src = new BitmapImage();
Output.Source = src;
await src.SetSourceAsync(stream);
}
}
And this is my xaml code.
<Grid>
<ScrollViewer >
<TextBlock Name="ViewPageLabel"VerticalAlignment="center">Now page</TextBlock>
<TextBox x:Name="PageNumberBox" InputScope="Number" Width="30" Text="1" TextAlignment="Right" Margin="5,0,5,0"
AutomationProperties.LabeledBy="{Binding ElementName=ViewPageLabel}"/>
<TextBlock VerticalAlignment="Center">of <Run x:Name="PageCountText"/>.</TextBlock>
</ScrollViewer>
</Grid>
Is there any suggestions?
Please help me.
Thanks for reading this.
Ran into this issue recently, didn't see any definitive answer here, so here it goes:
JosephA is on the right track, but PdfPageRenderOptions does not have anything about image fidelity/quality or anything like that like I had hoped. However, it does allow you to specify image dimensions. All you have to do is scale up the dimensions.
I suspect what's going on is that the PDF has a scale that it would "like" to use, which is smaller than the image you want to draw. Without specifying dimensions, it's drawing the "small" image and then it's getting scaled up, causing the blurriness.
Instead, if you tell it to draw the image at a higher resolution explicitly, it will do PDF magic to make those lines crisper, and the resulting raster image won't have to scale/blur.
PdfPage page = _document.GetPage(pageIndex);
await page.RenderToStreamAsync(stream, new PdfPageRenderOptions {
DestinationWidth = (uint)page.Dimensions.MediaBox.Width * s,
DestinationHeight = (uint)page.Dimensions.MediaBox.Height * s
});
Something like this helps, where "s" is the scale factor to achieve the dimensions you need.
PdfPage.Dimensions has a number of different properties other than just "MediaBox" that you may want to explore as well depending on your use case.
It sounds like you are encountering the common problem where you need to dynamically adjust the resolution the PDF page is rendered at to an image.
I am not familiar with the that PDF library however it appears you need to use the RenderToStreamAsync() overload that takes a PdfPageRenderOptions parameter to accomplish this.
Related
I am implementing a Universal Windows Application on Windows 10 using MVVM.
I have a file picker allowing me to choose an image. This image is displayed in an Image Control. The source of the Image Control is bound to a property in my view model. This property is a byte array. I need a converter to convert the BitmapImage in a Byte Array.
I have read lots of things but can't found something working.
I have found interesting stuff on https://writeablebitmapex.codeplex.com/
but if i want to use this package i need a WriteableBitmap and not a BitmapImage.
Thank you in advance for your help.
You can load the image to a WriteableBitmap object straight from the file.
var filePicker = new FileOpenPicker();
filePicker.FileTypeFilter.Add(".jpg");
var result = await filePicker.PickSingleFileAsync();
if (result != null)
{
using (IRandomAccessStream stream = await result.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
WriteableBitmap bmp = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
bmp.SetSource(stream);
// show the image in the UI if you want.
MyImage.Source = bmp;
}
}
This way you have the WriteableBitmap and you can use the WriteableBitmapEx library.
I found many samples of displaying images from a resource in a Windows Store app and got it to display images within a sample, but I would require the flipview to show images in a directory, or at least to show image file names I provide by code. With everything I tried so far the flipview remains empty. I maybe missing something obvious, this is the relevant part of the XAML:
<FlipView x:Name="flipView1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="809,350,9,7" Width="548" Height="411" >
<FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=Image }" Stretch="Uniform"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
this works, but it requires me to add the images a resource first....
ImageBrush brush1 = new ImageBrush();
brush1.ImageSource = new BitmapImage(new Uri("ms-appx:///Assets/P1000171.jpg"));
FlipViewItem flipvw1 = new FlipViewItem();
flipvw1.Background = brush1;
flipView1.Items.Add(flipvw1);
but (for example) this doesn't:
string name = String.Format(#"c:\temp\P1000171.JPG");
Uri uri = new Uri(name);
BitmapImage img = new BitmapImage(uri);
flipView1.Items.Add(img);
What do I miss?
In the meantime I've found the answer myself which I now add for future readers. The above example won't work because a Windows 8 app isn't allowed to access most of the PC's directories without the user having selected one using a FolderPicker. The program can re-use that directory later with:
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
I've changed the above XAML here:
<Image Source="{Binding}" Stretch="UniformToFill"/>
The Task below will show all .JPG files in the Pictures library in a Flipview, if, in the Package.appxmanifest, Capabilities, "Pictures library" has been checked:
public async Task flipviewload()
{
IReadOnlyList<StorageFile> fileList = await picturesFolder.GetFilesAsync();
var images = new List<BitmapImage>();
if (fileList != null)
{
foreach (StorageFile file in fileList)
{
string cExt = file.FileType;
if (cExt.ToUpper()==".JPG")
{
Windows.Storage.Streams.IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
using (Windows.Storage.Streams.IRandomAccessStream filestream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
images.Add(bitmapImage);
}
}
}
}
flpView.ItemsSource = images;
}
I need to load 8 images into Image or BitmapImage elements in my C# Windows Store (Windows RT) app, and need to find their dimensions (even before they are being displayed). I can download the images, but can't really figure out how to get the size (size is always zero). The reason size is zero is that the bitmap image has not loaded, but can't figure out how to wait till the image is loaded, catch the function that indicates image has loaded.
Here is the key code, where the ImageOpened or ImageFailed never get called:
bm.ImageOpened += bm_ImageOpened;
bm.ImageFailed += bm_ImageFailed;
bm = new BitmapImage(new Uri(arbitraryImageUriThatKeepsChanging, UriKind.Absolute));
image.ImageFailed += image_ImageFailed;
image.ImageOpened += image_ImageOpened;
image.Source = new BitmapImage(new Uri(arbitraryImageUriThatKeepsChanging,
UriKind.Absolute));
/* .. at this point the image still has not been loaded,
so can't find the dimensions or if it failed or not, and the functions to catch
image opened and failed are never called..*/
All I need is to load images from external web site, and find the dimensions, before displaying the images.
You could just use a dummy-Imageelement that is invisible to preload the image.
In your Xaml:
<Image Name="DummyImage" Visibility="Collapsed"/>
Code behind:
BitmapImage btm = new BitmapImage(new Uri("http://anyurl.com/foo.jpg"));
btm.DownloadProgress += btm_DownloadProgress;
DummyImage.Source = btm;
private void btm_DownloadProgress(object sender, DownloadProgressEventArgs e)
{
if (e.Progress == 100)
{
int width = ((BitmapImage)sender).PixelWidth;
}
}
You should be able to use either the DownloadProgress or the ImageLoaded event.
This is the code I am using for binding image in XAML
<Border toolkit:TiltEffect.IsTiltEnabled="true" Height="350" Width="400" Grid.ColumnSpan="3">
<Grid Height="350" Width="400" Margin="70,0,70,0" x:Name="Container1">
<Grid.Background>
<ImageBrush ImageSource="{Binding ImageCollection[0]}" Stretch="Uniform" AlignmentX="Left" AlignmentY="Center"/>
</Grid.Background>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ImageTapCommand}" CommandParameter="CONTAINER0"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</Border>
Likewise I am using 4 border for displaying my recent images.
In my ViewModel I am using the below method for reading image from isolated storage.
public Stream GetFileStream(string filename, ImageLocation location)
{
try
{
lock (SyncLock)
{
if (location == ImageLocation.RecentImage)
{
filename = Constants.IsoRecentImage + #"\" + filename;
}
using (var iSf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!iSf.FileExists(filename)) return null;
var fs = iSf.OpenFile(filename, FileMode.Open, FileAccess.Read);
return fs;
}
}
}
catch (Exception ex)
{
return null;
}
}
And after getting the stream I will use this below written method four building the WritableBitmap for UI binding
private WriteableBitmap BuildImage(Stream imageStream)
{
using (imageStream)
{
var image = new BitmapImage();
image.SetSource(imageStream);
return new WriteableBitmap(image);
}
}
In this case my issue is after navigating to and from my page for two - three times. The app crashes on BuildImage() method where I am using " image.SetSource(imageStream);" method. I tried many alternatives but failed. The exception I am getting is "System.OutOfMemoryException "
I tried Image control instead of Image brush.
I tried Bitmap instead of WritableBitmap etc. but the result is same.
The app crashing rate will reduce if I use small images. But the crash rate is high with the images taken through camera.
I am trying for a solution to this issue for last one week, But didn't find any alternative to fix the issue.
I found a link that talks about similar issue But didn't get many to solve the issue
Try this,
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
bitmapImage.CreateOptions = BitmapCreateOptions.None;
var bmp = new WriteableBitmap((BitmapSource) bitmapImage);
bitmapImage.UriSource = (Uri) null;
return bmp;
Silverlight caches images by default to improve performance. You should call
image.UriSource = null after using the BitmapImage to dispose of the resources.
Are you reseting/disposing the IsolatedStorageFileStream and the IsolatedStorageFile after you use them?
Have you tried forcing the garbage collector to run to see if that makes any difference.
GC.Collect();
This is not intended as a solution - you should never have to call GC.Collect, but it might help determine whether your problem is a memory leak or just a delay in the memory being reclaimed.
I'm currently writing a portrait only app, but I have a customer requirement that they'd like to implement a special feature if the phone is turned on its side.
To be clear they don't want the page to change orientation - so keeping the page as portrait works well here - but they do want to be able to detect the sideways change.
Is there anyway of finding this out (e.g. from rootframe or from some other object?) or do I have to access the Accelerometer data and work it out myself?
To be clear on this...
I'm trying to keep the page in portrait at all times.
and if I specify SupportedOrientations="portraitorlandscape" then keeping the page in portrait seems to be hard (correct me if I'm wrong, but it just doesn't seem to want to stay in portrait - the MS SDK is too good at making the page go landscape)
and if I don't specify SupportedOrientations="portraitorlandscape" then I don't get calls to OnOrientationChanged in either the page or the RootFrame
And as the icing on the cake... I need the phone to stay in portrait mode too - I need the SystemTray to stay at the top of the screen (the portrait top).
You can handle the OnOrientationChanged event which will return a PageOrientation enumeration.
Accepting this because of the comments:
#Stuart - You may find the Orientation Helper class in this starter kit useful. It uses the accelerometer, so I guess you'll have to use that, but it might save you time rolling out your own version: http://msdn.microsoft.com/en-us/library/gg442298%28VS.92%29.aspx#Customizing_Behavior
This might help, but for a case when arriving to this particular page, not for the initial page - so only partly answering the question. It trigs the OnOrientationChanged although no change has been done! (Figured out this solution after having tried to find a solution for two days) :
On the particular page, write in . xaml code
Orientation="None"
On the .xaml.cs side, write under
InitializeComponent();
Orientation = this.Orientation;
this.OrientationChanged += new EventHandler<OrientationChangedEventArgs>
(OnOrientationChanged);
and separately
void OnOrientationChanged(object sender, OrientationChangedEventArgs e)
{
if ((e.Orientation & PageOrientation.Landscape) != 0)
{
MyImage.Height = 480; //for example
}
{
MyImage.Width = 480; // for example
}
}
In my case, I placed the image as follows:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel>
<Image x:Name="MyImage"/>
</StackPanel>
.. followed by other code, still loading during which time picture is shown ...
This decreases the size of the image when in Landscape mode when entering the page!
Got the solution, finally, after having seen Jeff Prosises site
Detect orientation change:
http://alan.beech.me.uk/2011/04/19/detecting-orientation-change-wp7dev/
I had to do a similar thing in one of my apps before where an image that is used as the background doesnt rotate but other items on the page do.
The code looks a bit like this:
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
// Keep the image in the same position as in portrait
// But still allows other controls to rotate when orientation changes.
switch (e.Orientation)
{
case PageOrientation.LandscapeRight:
ForegroundImage.RenderTransform = new CompositeTransform { Rotation = 90 };
ForegroundImage.RenderTransformOrigin = new Point(0.5, 0.5);
ForegroundImage.Margin = new Thickness(158.592, -158.792, 158.592, -160.558);
break;
case PageOrientation.LandscapeLeft:
ForegroundImage.RenderTransform = new CompositeTransform { Rotation = 270 };
ForegroundImage.RenderTransformOrigin = new Point(0.5, 0.5);
ForegroundImage.Margin = new Thickness(158.592, -158.792, 158.592, -160.558);
break;
default: // case PageOrientation.PortraitUp:
ForegroundImage.RenderTransform = null;
ForegroundImage.RenderTransformOrigin = new Point(0, 0);
ForegroundImage.Margin = new Thickness();
break;
}
base.OnOrientationChanged(e);
}
Unfortunately there's no real work around for the system tray or app bar. For the system tray you could hide this though and then only show it (for a period of time) when the user taps or swipes near that part of the screen.