How is iBooks rendering the index view so quickly when viewing a PDF? Or: how do draw UIImages on UIScrollView directly without UIImageView? - objective-c

I have a 140 pages test PDF (the full Adobe PDF specification, http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/adobe_supplement_iso32000.pdf) and open it in iBooks. Then switch to index (thumbnail) view. If I scroll to the end fast enough I can see that I can scroll faster than iBooks renders the pages but it catches up pretty quickly on iPad 2.
I learn two things from this:
First iBooks is showing 140 empty squares in the right size and then populates the preview.
iBooks really renders all of the previews and keeps them in memory (if I scroll around I cannot spot any re-rendering)
I also tested with another Adobe Spec that has 700+ pages: exactly same behavior! Fascinating!
The question is how are they doing it? I wrote some code that gets each page of the PDF as an image, adds it to a UIImageView and adds that to the scrollview.
I use the same technique and layout as iBooks does. It renders just as quick as iBooks but memory consumption is insane and especially when scrolling the app gets totally stuck after a while. Can anybody point me in the right direction? I already removed the PDF rendering for testing and it is really fast, so I can pin it down to the thumbnail generation.
EDIT:
If from the code below the PDF generation is removed and an empty UIImageView is returned instead, the performance is still extremely weak. So my assumption is that the UIImageView is causing the problem. How can I draw the PDF thumbs onto my UIScrollView without the requirement of 140 UIImageViews?
For those firm in Monotouch, here's the code I'm using to render the thumbs, maybe it shows an obvious weakness:
/// <summary>
/// Gets the low res page preview of a PDF page. Does a quick image render of the page.
/// </summary>
/// <param name="iPage">the number of the page to render</param>
/// <param name="oTergetRect">the target rect to fit the PDF page into</param>
/// <returns>
/// The low res page image view.
/// </returns>
public static UIImageView GetLowResPagePreview (CGPDFPage oPdfPage, RectangleF oTargetRect)
{
RectangleF oOriginalPdfPageRect = oPdfPage.GetBoxRect (CGPDFBox.Media);
RectangleF oPdfPageRect = PdfViewerHelpers.RotateRectangle( oPdfPage.GetBoxRect (CGPDFBox.Media), oPdfPage.RotationAngle);
// If preview is requested for the PDF index view, render a smaller version.
if (!oTargetRect.IsEmpty)
{
// Resize the PDF page so that it fits the target rectangle.
oPdfPageRect = new RectangleF (new PointF (0, 0), GetFittingBox (oTargetRect.Size, oPdfPageRect.Size));
}
// Create a low res image representation of the PDF page to display before the TiledPDFView
// renders its content.
int iWidth = Convert.ToInt32 ( oPdfPageRect.Size.Width );
int iHeight = Convert.ToInt32 ( oPdfPageRect.Size.Height );
CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB();
CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedLast);
// First fill the background with white.
oContext.SetFillColor (1.0f, 1.0f, 1.0f, 1.0f);
oContext.FillRect (oOriginalPdfPageRect);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
oContext.ConcatCTM ( oPdfPage.GetDrawingTransform ( CGPDFBox.Media, oPdfPageRect, 0, true ) );
oContext.DrawPDFPage (oPdfPage);
CGImage oImage = oContext.ToImage();
UIImage oBackgroundImage = UIImage.FromImage( oImage );
oContext.Dispose();
oImage.Dispose ();
oColorSpace.Dispose ();
UIImageView oBackgroundImageView = new UIImageView (oBackgroundImage);
oBackgroundImageView.Frame = new RectangleF (new PointF (0, 0), oPdfPageRect.Size);
oBackgroundImageView.ContentMode = UIViewContentMode.ScaleToFill;
oBackgroundImageView.UserInteractionEnabled = false;
oBackgroundImageView.AutoresizingMask = UIViewAutoresizing.None;
return oBackgroundImageView;
}
internal static RectangleF RotateRectangle ( RectangleF oRect, int iRotationAngle )
{
if ( iRotationAngle == 90 || iRotationAngle == 270 )
{
return new RectangleF (oRect.X, oRect.Y, oRect.Height, oRect.Width);
}
return oRect;
}

You shouldn't be using 140 UIImageViews !!! use only just enough to fill the area and then recycle the ones that are no longer displayed.
How did Apple implement UITableView ?? Do you think they keep all tableview cells in memory??
Look at the PhotoScroller sample code and the corresponding WWDC 2010 video. I think it is named "Desigining apps with scrollViews"
WWDC 2011 video of similar name is continuation of the same trick of view reuse.
Hope this helps.

Have you checked the size of each UIImageView? Perhaps each of your thumbnails is actually the size of a full page.
Perhaps iBooks doesn't put each thumbnail in a UIImageView? Maybe the app is using something from CoreAnimation or even OpenGL ES?

Related

why does ImageResizer is rotating automaticlly image photographed vertical and show it horizontal

Hello i Have uploaded image using ImageResizer
using this code
var instructions = new ImageResizer.Instructions
{
Width = Convert.ToInt32(Newwidth),
Height = Convert.ToInt32(Newheiht),
Format = "jpg",
Mode = ImageResizer.FitMode.Crop,
Scale = ImageResizer.ScaleMode.Both,
};
File.InputStream.Seek(0, SeekOrigin.Begin);
ImageResizer.ImageJob i = new ImageResizer.ImageJob(File, Filepath, instructions);
i.CreateParentDirectory = false;
i.Build();
i have tried using
AutoRotate = true
i still got the image rotated to horizontall
i also tried Rotate=90
the pic was rotated and lost the proportion here is how it looks like after rotate
the resized roteted picture changed it to width=296 height=437 instead of height=296 width=437
the image was taken vertically using samsung galaxy 3 buy when resized it and upload it it show horizonally
here is the original photo taken:
here is how it shown after uploaded:
You haven't posted the diagnostics page, which lists which plugins are installed.
The most likely cause is that you didn't <add name="AutoRotate" /> or new AutoRotate().Install(Config.Current) to install the AutoRotate plugin, and therefore &autorotate=true had no effect.
It's also possible that there's a new form of image metadata that we can't detect yet, but that seems less likely.
Try Autorotate property "True"; It worked for me.
&autorotate=true

iTextSharp rotated PDF page reverts orientation when file is rasterized at print house

Using iTextSharp I am creating a PDF composed of a collection of existing PDFs, some of the included PDFs are landscape orientation and need to be rotated. So, I do the following:
private static void AdjustRotationIfNeeded(PdfImportedPage pdfImportedPage, PdfReader reader, int documentPage)
{
float width = pdfImportedPage.Width;
float height = pdfImportedPage.Height;
if (pdfImportedPage.Rotation != 0)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(0));
}
if (width > height)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(270));
}
}
This works great. The included PDFs rotated to portrait orientation if needed. The PDF prints correctly on my local printer.
This file is sent to a fulfillment house, and unfortunately, the landscape included files do not print properly when going through their printer and rasterization process. They use Kodak (Creo) NexRip 11.01 or Kodak (Creo) Prinergy 6.1. machines. The fulfillment house's suggestion is to: "generate a new PDF file after we rotate pages or make any changes to a PDF. It is as easy as exporting out to a PostScript and distilling back to a PDF."
I know iTextSharp doesn't support PostScript. Is there another way iTextSharp can rotate included PDFs to hold the orientation when rasterized?
First let me assure you that changing the rotation in the page dictionary is the correct procedure to achieve what you want. As far as I can see your code, there's nothing wrong with it. You are doing the right thing.
Unfortunately, you are faced with a third party product over which you have no control that is not doing the right thing. How to solve this?
I have written an example called IncorrectExample. I have named it that way because I don't want it to be used in a context that is different from yours. You can safely ignore all the warnings I added: they are not meant for you. This example is very specific to your problem.
Please try the following code:
public void manipulatePdf(String src, String dest)
throws IOException, DocumentException {
// Creating a reader
PdfReader reader = new PdfReader(src);
// step 1
Rectangle pagesize = getPageSize(reader, 1);
Document document = new Document(pagesize);
// step 2
PdfWriter writer
= PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
pagesize = getPageSize(reader, i);
document.setPageSize(pagesize);
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
if (isPortrait(reader, i)) {
cb.addTemplate(page, 0, 0);
}
else {
cb.addTemplate(page, 0, 1, -1, 0, pagesize.getWidth(), 0);
}
}
// step 4
document.close();
reader.close();
}
public Rectangle getPageSize(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSizeWithRotation(pagenumber);
return new Rectangle(
Math.min(pagesize.getWidth(), pagesize.getHeight()),
Math.max(pagesize.getWidth(), pagesize.getHeight()));
}
public boolean isPortrait(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSize(pagenumber);
return pagesize.getHeight() > pagesize.getWidth();
}
I have taken the pages.pdf file as an example. This file is special in the sense that it has two pages in landscape that are created in a different way:
one page is a page of which the width is smaller than the height (sounds like it's a page in portrait), but as there's a /Rotate value of 90 added to the page dictionary, it is shown in landscape.
the other page isn't rotated, but it has a height that is smaller than the width.
In my example, I am using the classes Document and PdfWriter to create a copy of the original document. This is wrong in general because it throws away all interaction. I should use PdfStamper or PdfCopy instead, but it is right in your specific case because you don't need the interactivity: the final purpose of the PDF is to be printed.
With Document, I create new pages using a new Rectangle that uses the lowest value of the dimensions of the existing page as the width and the highest value as the height. This way, the page will always be in portrait. Note that I use the method getPageSizeWithRotation() to make sure I get the correct width and height, taking into account any possible rotation.
I then add a PdfImportedPage to the direct content of the writer. I use the isPortrait() method to find out if I need to rotate the page or not. Observe that the isPortrait() method looks at the page size without taking into account the rotation. If we did take into account the rotation, we'd rotate pages that don't need rotating.
The resulting PDF can be found here: pages_changed.pdf
As you can see, some information got lost: there was an annotation on the final page: it's gone. There were specific viewer preferences defined for the original document: they're gone. But that shouldn't matter in your specific case, because all that matters for you is that the pages are printed correctly.

Crop / Clip Images in WinRT [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
How I can Crop / Clip images in WinRT. I have an image filled completely in a windows 8 window. I need to clip / crop the image from center and i need to display the two image section into two separate grids. How I can do the same through windows 8. Is it possible to implement this without using WritableBitmapEx. If no, how to do the same through WritableBitmapEx.
There are many ways to do it actually, each with some pros and cons.
WriteableBitmapEx seems like a popular solution. I have a similar implementation in WinRT XAML Toolkit. Both are essentially copying blocks of pixels from a full image bitmap. It might not be the fastest way, but if you'd want to get an out of the box solution - it is one that is easy to use. You need to copy the pixels, so you are not optimizing for memory use at the time of the operation and so might run out of memory on very large images quicker. You can recrop easily though end save the results to an image file if you want.
The BitmapDecoder solution Jan recommended is one I often use as it is part of the platform, written in native code and possibly highly optimized and you don't copy the pixels, but if you want to recrop - you'll need to decode the image again.
Xyroid's suggestion with Clip geometry is a quick display-only solution. You don't actually modify the bitmap in memory - you simply display a region of it on the screen. You need then to keep the entire image in memory and if you want to save it - you still need to update the bitmap to save it - by using either one of the first two solutions or maybe use RenderTargetBitmap.Render() if screen resolution is enough for you. It should be very quick though to update the crop region displayed on the screen for quick preview.
Another one is with a Rectangle filled with an ImageBrush where you can apply a Transform and specify the Rectangle size to control cropping. It is fairly similar to the Clip solution only instead of clipping an image and in this case you actually have to use the Tramsform (which you can also do on a Clip - RectangleGeometry). For quick updates - using a Transform might actually be a bit faster than updating the geometry and also supports scaling and rotations.
You can use the Bitmapdecoder and the BitmapTransform classes. This example is very good for cropping. You should also read this tutorial for clipping. Basically you implement a function like this (taken from the example):
async public static Task<ImageSource> GetCroppedBitmapAsync(StorageFile originalImgFile, Point startPoint, Size corpSize, double scale)
{
if (double.IsNaN(scale) || double.IsInfinity(scale))
{
scale = 1;
}
// Convert start point and size to integer.
uint startPointX = (uint)Math.Floor(startPoint.X * scale);
uint startPointY = (uint)Math.Floor(startPoint.Y * scale);
uint height = (uint)Math.Floor(corpSize.Height * scale);
uint width = (uint)Math.Floor(corpSize.Width * scale);
using (IRandomAccessStream stream = await originalImgFile.OpenReadAsync())
{
// Create a decoder from the stream. With the decoder, we can get
// the properties of the image.
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
// The scaledSize of original image.
uint scaledWidth = (uint)Math.Floor(decoder.PixelWidth * scale);
uint scaledHeight = (uint)Math.Floor(decoder.PixelHeight * scale);
// Refine the start point and the size.
if (startPointX + width > scaledWidth)
{
startPointX = scaledWidth - width;
}
if (startPointY + height > scaledHeight)
{
startPointY = scaledHeight - height;
}
// Create cropping BitmapTransform and define the bounds.
BitmapTransform transform = new BitmapTransform();
BitmapBounds bounds = new BitmapBounds();
bounds.X = startPointX;
bounds.Y = startPointY;
bounds.Height = height;
bounds.Width = width;
transform.Bounds = bounds;
transform.ScaledWidth = scaledWidth;
transform.ScaledHeight = scaledHeight;
// Get the cropped pixels within the bounds of transform.
PixelDataProvider pix = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
transform,
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.ColorManageToSRgb);
byte[] pixels = pix.DetachPixelData();
// Stream the bytes into a WriteableBitmap
WriteableBitmap cropBmp = new WriteableBitmap((int)width, (int)height);
Stream pixStream = cropBmp.PixelBuffer.AsStream();
pixStream.Write(pixels, 0, (int)(width * height * 4));
return cropBmp;
}
}
XAML static way, if my screen size is 1366x768 & I want to clip center 400x300 image then I would do this.
<Image Source="Assets/img100.png" Stretch="Fill">
<Image.Clip>
<RectangleGeometry Rect="483,234,400,300" />
</Image.Clip>
</Image>
Dynamic way. It will make center clipping for all resolution, though height & width is fixed.
double _Height = 300, _Width = 400;
img.Clip = new RectangleGeometry
{
Rect = new Rect((Window.Current.Bounds.Width - _Width) / 2, (Window.Current.Bounds.Height - _Height) / 2, _Width, _Height)
};
Don't forget to checkout...
How to resize Image in C# WinRT/winmd?
Crop image with rectangle
Crop image with dynamic rectangle coordinate
Cropping tool after file picker (like the one after you take a picture)

iTextSharp Overlay Image

Hi guys I have an instance where I have a logo image as part of some artwork..
If a user uploads a new logo I have a form field which is larger than the default logo.
I then use that form field to position the new image.
The problem is I need to set the background colour of that form field to white so that it covers the old logo in the event that the new image is smaller than the old logo..
what I have done is:
foreach (var imageField in imageReplacements)
{
fields.SetFieldProperty(imageField.Key, "bgcolor", iTextSharp.text.Color.WHITE, null);
fields.RegenerateField(imageField.Key);
PdfContentByte overContent = stamper.GetOverContent(imageField.Value.PageNumber);
float[] logoArea = fields.GetFieldPositions(imageField.Key);
if (logoArea != null)
{
iTextSharp.text.Rectangle logoRect = new iTextSharp.text.Rectangle(logoArea[1], logoArea[2], logoArea[3], logoArea[4]);
var logo = iTextSharp.text.Image.GetInstance(imageField.Value.Location);
if (logo.Width >= logoRect.Width || logo.Height >= logoRect.Height)
{
logo.ScaleToFit(logoRect.Width, logoRect.Height);
}
logo.Alignment = iTextSharp.text.Image.ALIGN_LEFT;
logo.SetAbsolutePosition(logoRect.Left, logoArea[2] + (logoRect.Height - logo.ScaledHeight) / 2);
// left: logoArea[3] - logo.ScaledWidth + (logoRect.Width - logo.ScaledWidth) / 2
overContent.AddImage(logo);
}
}
The problem with this is that the background colour of the field is set to white and the image then doesn't appear.. i remove the SetFieldProperty and RegenerateField commands and the image replacement works fine..
is there a way to set a stacking order on layers?
Annotations (such as form fields) are always on top of page contents. Annotation Z order is just the order of the annotations array on a given page.
Page content Z order is just the order everything appears in the content stream. New drawing operators go on top of proceeding operators.
If you want to cover your old image, draw a white box over it and then draw the new logo over top that. No need to worry about annotations.
Actually, all you really need to do is not set the background color of the imageField. You're already scaling the new logo to match the size of the old one.
However, if you really must draw that white box, it's fairly simple:
overContent.setColorFill(iTextSharp.text.Color.WHITE);
overContent.rectangle( logoRect );
overcontent.fill();

Ready An Existing PDF Page Size (ex. 8.5 x 11, 11 x 17) VB.Net

Like the title says i'd like to read an existing pdf page size with VB.Net. I've been working with Itext.Sharp, and the Acrobat.dll. Is this possible??
There are a number of different "Boxes" a given page can have:
Media Box (required): The initial page size when printing viewing.
Crop Box (optional): Supersedes the media box. Defaults to match the media box. Must be a subset or match the media box.
There's also art/trim/bleed boxes, but they don't matter as much and are much less common.
So, the page size:
PdfReader reader = new PdfReader(myPath);
// gets the MEDIA BOX
Rectangle pageRect = reader.getPageSize(1); // 1 -> first page
// gets the crop box if present, or the media box if not.
Rectangle cropRect = reader.getCropBox(1);
// and finally
Rectangle artBox = reader.getBoxSize( 1, "art");
// could be "art", "bleed", "crop", "media", or "trim"
I'd go with getCropBox().
I also recommend checking out the JavaDoc for things like this. At the very least you would have come up with getPageSize() on your own. No, it's not C#. Yes, it's very useful.
http://api.itextpdf.com/
Also note that these Rectangles need not be based on 0,0 (which would be the lower left corner on an unrotated page).
Further, you should check the page's rotation, getPageRotation(int), and swap height and width if the rotation is 90 or 270. There is getPageSizeWithRotation(int), but it only works with the media box, so I'd do it yourself if I were you. It's only a few extra lines of code:
// rotation has to be 0, 90, 180, or 270. "360" isn't kosher IIRC.
if (reader.getPageRotation(pageNum) % 180 != 0) {
float tmp = width;
width = height;
height = tmp;
}