public async Task MakeSureNothingRenderedWhenImageCached() { // The exact image we need is in the cache. So we should never make a // request to load the PDF file or PdfDocument. // Get the infrastructure setup var f = new dummyFile(); var data = await TestUtils.GetFileAsBytes("test.pdf"); int loaderCalled = 0; f.GetStream = () => { loaderCalled++; throw new InvalidOperationException(); }; // Create the cache, and add everything into it that the system should need. var dc = new dummyCache(); await f.SaveFileInCache(f.DateToReturn, data, dc); var dt = await f.GetCacheCreateTime(dc); var pageSize = new IWalkerSize() { Width = 1280, Height = 720 }; await dc.InsertObject(string.Format("{0}-{1}-p1-DefaultPageSize", f.UniqueKey, dt.Value.ToString()), pageSize); var imageData = new byte[] { 0, 1, 2, 3, 4 }; await dc.Insert(string.Format("{0}-{1}-p1-w100-h56", f.UniqueKey, dt.Value), imageData); Debug.WriteLine("Setup is done, and data has been inserted into the cache. Testing starting"); // Create the rest of the infrastructure. var vm = new FileDownloadController(f, dc); var pf = new PDFFile(vm); // Now, build the VM var pdfVM = new PDFPageViewModel(pf.GetPageStreamAndCacheInfo(1), dc); // Subscribe so we can "get" the image. MemoryStream lastImage = null; pdfVM.ImageStream.Subscribe(img => lastImage = img); Assert.IsNull(lastImage); // Render, and make sure things "worked" pdfVM.RenderImage.Execute(Tuple.Create(IWalker.ViewModels.PDFPageViewModel.RenderingDimension.Horizontal, (double)100, (double)100)); vm.DownloadOrUpdate.Execute(null); await TestUtils.SpinWait(() => lastImage != null, 2000); Assert.AreEqual(0, loaderCalled); Assert.IsNotNull(lastImage); Assert.AreEqual(4, dc.NumberTimesInsertCalled); // Nothing new should have happened }
/// <summary> /// Initialize with the page that we should track. /// </summary> /// <param name="pageInfo">A stream of cache tags and the PDF pages associated with it.</param> /// <remarks>We are really only interested in the first PdfPage we get - and we will re-subscribe, but not hold onto it.</remarks> public PDFPageViewModel(IObservable <Tuple <string, IObservable <PdfPage> > > pageInfo, IBlobCache cache = null) { _cache = cache ?? Blobs.LocalStorage; // Render an image when: // - We have at least one render request // - ImageStream is subscribed to // - the file or page updates somehow (new pageInfo iteration). // // Always pull from the cache first, but if that doesn't work, then render and place // in the cache. // Get the size of the thing when it is requested. This sequence must be finished before // any size calculations can be done! var imageSize = from pgInfo in pageInfo from sz in _cache.GetOrFetchPageSize(pgInfo.Item1, () => pgInfo.Item2.Take(1).Select(pdf => pdf.Size.ToIWalkerSize())) select new { PGInfo = pgInfo, Size = sz }; var publishedSize = imageSize .Do(info => _pageSize = info.Size) .Select(info => info.PGInfo) .Publish().RefCount(); _pageSizeLoaded = publishedSize.AsUnit(); // The render request must be "good" - that is well formed. Otherwise // we will just ignore it. This is because sometimes when things are being "sized" the // render request is malformed. RenderImage = ReactiveCommand.Create(); var renderRequest = RenderImage .Cast <Tuple <RenderingDimension, double, double> >() .Where(info => (info.Item1 == RenderingDimension.Horizontal && info.Item2 > 0) || (info.Item1 == RenderingDimension.Vertical && info.Item3 > 0) || (info.Item1 == RenderingDimension.MustFit && info.Item2 > 0 && info.Item3 > 0) ); // Generate an image when we have a render request and a everything else is setup. ImageStream = from requestInfo in Observable.CombineLatest(publishedSize, renderRequest, (pSize, rr) => new { pgInfo = pSize, RenderRequest = rr }) let imageDimensions = CalcRenderingSize(requestInfo.RenderRequest.Item1, requestInfo.RenderRequest.Item2, requestInfo.RenderRequest.Item3) from imageData in _cache.GetOrFetchPageImageData(requestInfo.pgInfo.Item1, imageDimensions.Item1, imageDimensions.Item2, () => requestInfo.pgInfo.Item2.SelectMany(pdfPg => { var ms = new MemoryStream(); var ra = WindowsRuntimeStreamExtensions.AsRandomAccessStream(ms); var opt = new PdfPageRenderOptions() { DestinationWidth = (uint)imageDimensions.Item1, DestinationHeight = (uint)imageDimensions.Item2 }; return(Observable.FromAsync(() => pdfPg.RenderToStreamAsync(ra).AsTask()) .Select(_ => ms.ToArray())); })) select new MemoryStream(imageData); }