/// <summary> /// If we are able to directly submit to YouTrack, we do that. But otherwise, /// this makes a zip file of everything we want to submit, in order to /// give the user a single thing they need to attach and send. /// </summary> private void MakeEmailableReportFile() { var filename = ("Report " + DateTime.UtcNow.ToString("u") + ".zip").Replace(':', '.'); filename = filename.SanitizeFilename('#'); var zipFile = TempFile.WithFilename(filename); _emailableReportFilePath = zipFile.Path; var zip = new BloomZipFile(_emailableReportFilePath); using (var file = TempFile.WithFilenameInTempFolder("report.txt")) { using (var stream = RobustFile.CreateText(file.Path)) { stream.WriteLine(GetFullDescriptionContents(false)); if (_includeBook.Visible && _includeBook.Checked) // only Visible if Book is not null { stream.WriteLine(); stream.WriteLine( "REMEMBER: if the attached zip file appears empty, it may have non-ascii in the file names. Open with 7zip and you should see it."); } } zip.AddTopLevelFile(file.Path); if (_includeBook.Visible && _includeBook.Checked) // only Visible if Book is not null { zip.AddDirectory(Book.FolderPath); if (WantReaderInfo()) { AddReaderInfo(zip); } AddCollectionSettings(zip); } } if (_includeScreenshot.Checked) { using (var file = TempFile.WithFilenameInTempFolder("screenshot.png")) { RobustImageIO.SaveImage(_screenshot, file.Path, ImageFormat.Png); zip.AddTopLevelFile(file.Path); } } if (Logger.Singleton != null) { try { using (var logFile = GetLogFile()) { zip.AddTopLevelFile(logFile.Path); } } catch (Exception) { // just ignore } } zip.Save(); }
private static void TryGetScreenshot(Control controlForScreenshotting) { SafeInvoke.InvokeIfPossible("Screen Shot", controlForScreenshotting, false, () => { try { var bounds = controlForScreenshotting.Bounds; var screenshot = new Bitmap(bounds.Width, bounds.Height); using (var g = Graphics.FromImage(screenshot)) { if (controlForScreenshotting.Parent == null) { g.CopyFromScreen(bounds.Left, bounds.Top, 0, 0, bounds.Size); // bounds already in screen coords } else { g.CopyFromScreen(controlForScreenshotting.PointToScreen(new Point(bounds.Left, bounds.Top)), Point.Empty, bounds.Size); } } _screenshotTempFile = TempFile.WithFilename(ScreenshotName); RobustImageIO.SaveImage(screenshot, _screenshotTempFile.Path, ImageFormat.Png); } catch (Exception e) { ResetScreenshotFile(); Logger.WriteError("Bloom was unable to create a screenshot.", e); } } ); }
/// <summary> /// When images are copied from LibreOffice, images that were jpegs there are converted to bitmaps for the clipboard. /// So when we just saved them as bitmaps (pngs), we dramatically inflated the size of user's image files (and /// this then led to memory problems). /// So the idea here is just to try and detect that we should would be better off saving the image as a jpeg. /// Note that even at 100%, we're still going to lose some quality. So this method is only going to recommend /// doing that if the size would be at least 50% less. /// </summary> public static bool ShouldChangeFormatToJpeg(Image image) { try { using (var safetyImage = new Bitmap(image)) //nb: there are cases (notably http://jira.palaso.org/issues/browse/WS-34711, after cropping a jpeg) where we get out of memory if we are not operating on a copy { using (var jpegFile = new TempFile()) using (var pngFile = new TempFile()) { RobustImageIO.SaveImage(image, pngFile.Path, ImageFormat.Png); SaveAsTopQualityJpeg(safetyImage, jpegFile.Path); var jpegInfo = new FileInfo(jpegFile.Path); var pngInfo = new FileInfo(pngFile.Path); // this is just our heuristic. const double fractionOfTheOriginalThatWouldWarrantChangingToJpeg = .5; return(jpegInfo.Length < (pngInfo.Length * (1.0 - fractionOfTheOriginalThatWouldWarrantChangingToJpeg))); } } } catch (OutOfMemoryException e) { NonFatalProblem.Report(ModalIf.Alpha, PassiveIf.All, "Could not attempt conversion to jpeg.", "ref BL-3387", exception: e); return(false); } }
protected void MakeSamplePngImageWithMetadata(string path, int width = 10, int height = 10) { var x = new Bitmap(width, height); RobustImageIO.SaveImage(x, path, ImageFormat.Png); x.Dispose(); using (var img = PalasoImage.FromFileRobustly(path)) { img.Metadata.Creator = "joe"; img.Metadata.CopyrightNotice = "Copyright 1999 by me"; RetryUtility.Retry(() => img.SaveUpdatedMetadataIfItMakesSense()); } }
// Make a thumbnail of the input image. newWidth and newHeight are both limits; the image will not // be larger than original, but if necessary will be shrunk to fit within the indicated rectangle. // If parameter 'backColor' is not Empty, we fill the background of the thumbnail with that color. public static bool GenerateThumbnail(string originalPath, string pathToProcessedImage, int newWidth, Color backColor) { using (var originalImage = PalasoImage.FromFileRobustly(originalPath)) { // check if it needs resized if (originalImage.Image.Width <= newWidth) { return(false); } // calculate dimensions var newW = (originalImage.Image.Width > newWidth) ? newWidth : originalImage.Image.Width; // allow for proper rounding from the division var newH = (newW * originalImage.Image.Height + (originalImage.Image.Width / 2)) / originalImage.Image.Width; var thumbnail = new Bitmap(newW, newH); var g = Graphics.FromImage(thumbnail); if (backColor != Color.Empty) { using (var brush = new SolidBrush(backColor)) { g.FillRectangle(brush, new Rectangle(0, 0, newW, newH)); } } Image imageToDraw = originalImage.Image; bool useOriginalImage = ImageUtils.AppearsToBeJpeg(originalImage); if (!useOriginalImage) { imageToDraw = MakePngBackgroundTransparent(originalImage); } var destRect = new Rectangle(0, 0, newW, newH); // Note the image size may change when the background is made transparent. // See https://silbloom.myjetbrains.com/youtrack/issue/BL-5632. g.DrawImage(imageToDraw, destRect, new Rectangle(0, 0, imageToDraw.Width, imageToDraw.Height), GraphicsUnit.Pixel); if (!useOriginalImage) { imageToDraw.Dispose(); } RobustImageIO.SaveImage(thumbnail, pathToProcessedImage); } return(true); }
/// <summary> /// Get the license from the metadata and save it. /// </summary> private static void UpdateBookLicenseIcon(Metadata metadata, string bookFolderPath) { var licenseImage = metadata.License.GetImage(); var imagePath = bookFolderPath.CombineForPath("license.png"); // Don't try to overwrite the license image for a template book. (See BL-3284.) if (RobustFile.Exists(imagePath) && BloomFileLocator.IsInstalledFileOrDirectory(imagePath)) { return; } try { if (licenseImage != null) { using (Stream fs = new FileStream(imagePath, FileMode.Create)) { RobustImageIO.SaveImage(licenseImage, fs, ImageFormat.Png); } } else { if (RobustFile.Exists(imagePath)) { RobustFile.Delete(imagePath); } } } catch (Exception error) { // BL-3227 Occasionally get The process cannot access the file '...\license.png' because it is being used by another process. // That's worth a toast, since the user might like a hint why the license image isn't up to date. // However, if the problem is a MISSING icon in the installed templates, which on Linux or if an allUsers install // the system will never let us write, is not worth bothering the user at all. We can't fix it. Too bad. if (BloomFileLocator.IsInstalledFileOrDirectory(imagePath)) { return; } NonFatalProblem.Report(ModalIf.None, PassiveIf.All, "Could not update license image (BL-3227).", "Image was at" + imagePath, exception: error); } }
/// <summary> /// Save the image (of any format) to a jpeg file with 100 quality /// Note that this is still going to introduce some errors if the input is a bitmap. /// </summary> /// <remarks>Will throw if the destination is locked and the user tells us to give up. </remarks> public static void SaveAsTopQualityJpeg(Image image, string destinationPath) { var jpgEncoder = ImageCodecInfo.GetImageDecoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid); var encoder = Encoder.Quality; //nb: there are cases (notably http://jira.palaso.org/issues/browse/WS-34711, after cropping a jpeg) where we get out of memory if we are not operating on a copy // Use a temporary file pathname in the destination folder. This is needed to ensure proper permissions are granted // to the resulting file later after FileUtils.ReplaceFileWithUserInteractionIfNeeded is called. That method may call // RobustFile.Replace which replaces both the file content and the file metadata (permissions). The result of that if we use // the user's temp directory is described in http://issues.bloomlibrary.org/youtrack/issue/BL-3954. using (var temp = TempFile.InFolderOf(destinationPath)) using (var safetyImage = new Bitmap(image)) { using (var parameters = new EncoderParameters(1)) { //0 = max compression, 100 = least parameters.Param[0] = new EncoderParameter(encoder, 100L); RobustImageIO.SaveImage(safetyImage, temp.Path, jpgEncoder, parameters); } SIL.IO.FileUtils.ReplaceFileWithUserInteractionIfNeeded(temp.Path, destinationPath, null); } }
/// <summary> /// Using YouTrackSharp here. We can't submit /// the report as if it were from this person, even if they have an account (well, not without /// asking them for credentials, which is just not gonna happen). So we submit with an /// account we created just for this purpose, "auto_report_creator". /// </summary> private bool SubmitToYouTrack() { try { ChangeState(State.Submitting); _youTrackConnection.Authenticate("auto_report_creator", "thisIsInOpenSourceCode"); _issueManagement = new IssueManagement(_youTrackConnection); _youTrackIssue = new Issue(); _youTrackIssue.ProjectShortName = _youTrackProjectKey; _youTrackIssue.Type = "Awaiting Classification"; _youTrackIssue.Summary = string.Format(Summary, _name.Text); _youTrackIssue.Description = GetFullDescriptionContents(false); _youTrackIssueId = _issueManagement.CreateIssue(_youTrackIssue); // this could all be done in one go, but I'm doing it in stages so as to increase the // chance of success in bad internet situations if (_includeScreenshot.Checked) { using (var file = TempFile.WithFilenameInTempFolder("screenshot.png")) { RobustImageIO.SaveImage(_screenshot, file.Path, ImageFormat.Png); AddAttachment(file.Path); } } if (Logger.Singleton != null) { try { using (var logFile = GetLogFile()) { AddAttachment(logFile.Path); } } catch (Exception e) { _youTrackIssue.Description += System.Environment.NewLine + "***Got exception trying to attach log file: " + e.Message; _issueManagement.UpdateIssue(_youTrackIssueId, _youTrackIssue.Summary, _youTrackIssue.Description); } } if (_includeBook.Visible && _includeBook.Checked) // only Visible if Book is not null { ChangeState(State.UploadingBook); using (var bookZip = TempFile.WithFilenameInTempFolder(_youTrackIssueId + ".zip")) { var progress = new StatusProgress(); try { var zip = new BloomZipFile(bookZip.Path); zip.AddDirectory(Book.FolderPath); if (WantReaderInfo()) { AddReaderInfo(zip); } AddCollectionSettings(zip); zip.Save(); } catch (Exception error) { _youTrackIssue.Description += System.Environment.NewLine + "***Error as ProblemReporterDialog attempted to zip up the book: " + error.Message; _issueManagement.UpdateIssue(_youTrackIssueId, _youTrackIssue.Summary, _youTrackIssue.Description); Logger.WriteEvent("*** Error as ProblemReporterDialog attempted to zip up the book. " + error.Message); // if an error happens in the zipper, the zip file stays locked, so we just leak it bookZip.Detach(); _shortErrorHtml += " Error Zipping Book "; throw; } try { string url = ProblemBookUploader.UploadBook(BloomS3Client.ProblemBookUploadsBucketName, bookZip.Path, progress); _youTrackIssue.Description += System.Environment.NewLine + url; _issueManagement.UpdateIssue(_youTrackIssueId, _youTrackIssue.Summary, _youTrackIssue.Description); } catch (Exception error) { Logger.WriteError(progress.LastError, error); _youTrackIssue.Description += System.Environment.NewLine + "***Got exception trying upload book: " + error.Message; _issueManagement.UpdateIssue(_youTrackIssueId, _youTrackIssue.Summary, _youTrackIssue.Description); _shortErrorHtml += " Uploading Book Failed "; } } } ChangeState(State.Success); return(true); } catch (Exception error) { Debug.Fail(error.Message); return(false); } }
/// <summary> /// For electronic books, we want to minimize the actual size of images since they'll /// be displayed on small screens anyway. So before zipping up the file, we replace its /// bytes with the bytes of a reduced copy of itself. If the original image is already /// small enough, we return its bytes directly. /// We also make png images have transparent backgrounds. This is currently only necessary /// for cover pages, but it's an additional complication to detect which those are, /// and doesn't seem likely to cost much extra to do. /// </summary> /// <returns>The bytes of the (possibly) reduced image.</returns> internal static byte[] GetBytesOfReducedImage(string filePath) { using (var originalImage = PalasoImage.FromFileRobustly(filePath)) { var image = originalImage.Image; int originalWidth = image.Width; int originalHeight = image.Height; var appearsToBeJpeg = ImageUtils.AppearsToBeJpeg(originalImage); if (originalWidth > kMaxWidth || originalHeight > kMaxHeight || !appearsToBeJpeg) { // Preserve the aspect ratio float scaleX = (float)kMaxWidth / (float)originalWidth; float scaleY = (float)kMaxHeight / (float)originalHeight; // no point in ever expanding, even if we're making a new image just for transparency. float scale = Math.Min(1.0f, Math.Min(scaleX, scaleY)); // New width and height maintaining the aspect ratio int newWidth = (int)(originalWidth * scale); int newHeight = (int)(originalHeight * scale); var imagePixelFormat = image.PixelFormat; switch (imagePixelFormat) { // These three formats are not supported for bitmaps to be drawn on using Graphics.FromImage. // So use the default bitmap format. // Enhance: if these are common it may be worth research to find out whether there are better options. // - possibly the 'reduced' image might not be reduced...even though smaller, the indexed format // might be so much more efficient that it is smaller. However, even if that is true, it doesn't // necessarily follow that it takes less memory to render on the device. So it's not obvious that // we should keep the original just because it's a smaller file. // - possibly we don't need a 32-bit bitmap? Unfortunately the 1bpp/4bpp/8bpp only tells us // that the image uses two, 16, or 256 distinct colors, not what they are or what precision they have. case PixelFormat.Format1bppIndexed: case PixelFormat.Format4bppIndexed: case PixelFormat.Format8bppIndexed: imagePixelFormat = PixelFormat.Format32bppArgb; break; } // OTOH, always using 32-bit format for .png files keeps us from having problems in BloomReader // like BL-5740 (where 24bit format files came out in BR with black backgrounds). if (!appearsToBeJpeg) { imagePixelFormat = PixelFormat.Format32bppArgb; } using (var newImage = new Bitmap(newWidth, newHeight, imagePixelFormat)) { // Draws the image in the specified size with quality mode set to HighQuality using (Graphics graphics = Graphics.FromImage(newImage)) { graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = SmoothingMode.HighQuality; using (var imageAttributes = new ImageAttributes()) { // See https://stackoverflow.com/a/11850971/7442826 // Fixes the 50% gray border issue on bright white or dark images imageAttributes.SetWrapMode(WrapMode.TileFlipXY); // In addition to possibly scaling, we want PNG images to have transparent backgrounds. if (!appearsToBeJpeg) { // This specifies that all white or very-near-white pixels (all color components at least 253/255) // will be made transparent. imageAttributes.SetColorKey(Color.FromArgb(253, 253, 253), Color.White); } var destRect = new Rectangle(0, 0, newWidth, newHeight); graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes); } } // Save the file in the same format as the original, and return its bytes. using (var tempFile = TempFile.WithExtension(Path.GetExtension(filePath))) { // This uses default quality settings for jpgs...one site says this is // 75 quality on a scale that runs from 0-100. For most images, this // should give a quality barely distinguishable from uncompressed and still save // about 7/8 of the file size. Lower quality settings rapidly lose quality // while only saving a little space; higher ones rapidly use more space // with only marginal quality improvement. // See https://photo.stackexchange.com/questions/30243/what-quality-to-choose-when-converting-to-jpg // for more on quality and https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level // for how to control the quality setting if we decide to (RobustImageIO has // suitable overloads). RobustImageIO.SaveImage(newImage, tempFile.Path, image.RawFormat); // Copy the metadata from the original file to the new file. var metadata = SIL.Windows.Forms.ClearShare.Metadata.FromFile(filePath); if (!metadata.IsEmpty) { metadata.Write(tempFile.Path); } return(RobustFile.ReadAllBytes(tempFile.Path)); } } } } return(RobustFile.ReadAllBytes(filePath)); }
private bool MakePngBackgroundTransparent(string originalPath, string pathToProcessedImage) { try { using (var originalImage = PalasoImage.FromFileRobustly(originalPath)) { //if it's a jpeg, we don't resize, we don't mess with transparency, nothing. These things //are scary in .net. Just send the original back and wash our hands of it. if (ImageUtils.AppearsToBeJpeg(originalImage)) { return(false); } using (var processedBitmap = MakePngBackgroundTransparent(originalImage)) { //Hatton July 2012: //Once or twice I saw a GDI+ error on the Save below, when the app 1st launched. //I verified that if there is an IO error, that's what you get (a GDI+ error). //I looked once, and the %temp%/Bloom directory wasn't there, so that's what I think caused the error. //It's not clear why the temp/bloom directory isn't there... possibly it was there a moment ago //but then some startup thread cleared and deleted it? (we are now running on a thread responding to the http request) Exception error = null; for (var i = 0; i < 3; i++) //try three times { try { error = null; RobustImageIO.SaveImage(processedBitmap, pathToProcessedImage, originalImage.Image.RawFormat); break; } catch (Exception e) { Logger.WriteEvent("***Error in RuntimeImageProcessor while trying to write image."); Logger.WriteEvent(e.Message); error = e; //in setting the sleep time, keep in mind that this may be one of 20 images //so if the problem happens to all of them, then you're looking 20*retries*sleep-time, //which will look like hung program. //Meanwhile, this transparency thing is actually just a nice-to-have. If we give //up, it's ok. Thread.Sleep(100); //wait a 1/5 second before trying again } } if (error != null) { throw error; //will be caught below } } } return(true); } //we want to gracefully degrade if this fails (as it did once, see comment in bl-2871) catch (TagLib.CorruptFileException e) { NonFatalProblem.Report(ModalIf.Beta, PassiveIf.All, "Problem with image metadata", originalPath, e); return(false); } catch (Exception e) { //while beta might make sense, this is actually //a common failure at the moment, with the license.png //so I'm setting to alpha. NonFatalProblem.Report(ModalIf.Alpha, PassiveIf.All, "Problem making image transparent.", originalPath, e); return(false); } }
// Make a thumbnail of the input image. newWidth and newHeight are both limits; the image will not be larger than original, // but if necessary will be shrunk to fit within the indicated rectangle. public static bool GenerateEBookThumbnail(string coverImagePath, string pathToProcessedImage, int thumbnailWidth, int thumbnailHeight, Color backColor) { using (var coverImage = PalasoImage.FromFileRobustly(coverImagePath)) { var coverImageWidth = coverImage.Image.Width; var coverImageHeight = coverImage.Image.Height; // We want to see a small border of background color, even if the image is a photo. const int kborder = 1; var availableThumbnailWidth = thumbnailWidth - (2 * kborder); var availableThumbnailHeight = thumbnailHeight - (2 * kborder); // Calculate how big the image can be while keeping its original proportions. // First assume the width is the limiting factor var targetImageWidth = (coverImageWidth > availableThumbnailWidth) ? availableThumbnailWidth : coverImage.Image.Width; var targetImageHeight = targetImageWidth * coverImageHeight / coverImageWidth; // if actually the height is the limiting factor, maximize height and re-compute the width if (targetImageHeight > availableThumbnailHeight) { targetImageHeight = availableThumbnailHeight; targetImageWidth = targetImageHeight * coverImageWidth / coverImageHeight; } // pad to center the cover image var horizontalPadding = (availableThumbnailWidth - targetImageWidth) / 2; var verticalPadding = (availableThumbnailHeight - targetImageHeight) / 2; var destRect = new Rectangle(kborder + horizontalPadding, kborder + verticalPadding, targetImageWidth, targetImageHeight); // the decision here is just a heuristic based on the observation that line-drawings seem to look better in nice square block of color, // while full-color (usually jpeg) books look better with a thin (or no) border. We could put this under user control eventually. Rectangle backgroundAndBorderRect; var appearsToBeJpeg = ImageUtils.AppearsToBeJpeg(coverImage); if (appearsToBeJpeg) { backgroundAndBorderRect = destRect; backgroundAndBorderRect.Inflate(kborder * 2, kborder * 2); } else { // or, if we decide to always deliver the full thing: backgroundAndBorderRect = new Rectangle(0, 0, thumbnailWidth, thumbnailHeight); } using (var thumbnail = new Bitmap(thumbnailWidth, thumbnailHeight)) using (var g = Graphics.FromImage(thumbnail)) using (var brush = new SolidBrush(backColor)) { g.FillRectangle(brush, backgroundAndBorderRect); lock (ConvertWhiteToTransparent) { var imageAttributes = appearsToBeJpeg ? null : ConvertWhiteToTransparent; g.DrawImage( coverImage.Image, // finally, draw the cover image destRect, // with a scaled and centered destination 0, 0, coverImageWidth, coverImageHeight, // from the entire cover image, GraphicsUnit.Pixel, imageAttributes); // changing white to transparent if a png } RobustImageIO.SaveImage(thumbnail, pathToProcessedImage); // PNG thumbnails created from jpeg files seem to often be way too big, so try to save them as jpeg // files instead if it saves space. See https://silbloom.myjetbrains.com/youtrack/issue/BL-5605. if (appearsToBeJpeg && Path.GetFileName(pathToProcessedImage) == "thumbnail.png") { var jpgPath = Path.ChangeExtension(pathToProcessedImage, "jpg"); RobustImageIO.SaveImage(thumbnail, jpgPath, ImageFormat.Jpeg); var infoPng = new FileInfo(pathToProcessedImage); var infoJpg = new FileInfo(jpgPath); //Debug.WriteLine(String.Format("thumbnail.png size={0}; thumbnail.jpg size={1} (using smaller)", infoPng.Length, infoJpg.Length)); if (infoJpg.Length < infoPng.Length) { File.Delete(pathToProcessedImage); } else { File.Delete(jpgPath); } } } } return(true); }
private void ProcessOrder(object stateInfo) { // called on threadpool thread var order = stateInfo as ThumbnailOrder; if (order == null) { return; } try { // This doesn't have an IsDisposed() method, so catch a possible exception. // Note that tests won't have a order.CancelToken set. if (order.CancelToken != null && order.CancelToken.IsCancellationRequested) { return; } } catch (ObjectDisposedException) { return; } Image pendingThumbnail = null; // Don't try to make thumbnails on the UI thread. It's easy to get into a situation like BL-6208, // where the UI thread is waiting for this lock, but another thread that holds the lock is waiting // to do something (e.g., navigate a browser) that can only be done on the UI thread. Debug.Assert(!Program.RunningOnUiThread, "Thumbnails must not be made on the UI thread"); lock (this) { Logger.WriteMinorEvent("HtmlThumbNailer ({1}): starting work on thumbnail ({0})", order.ThumbNailFilePath, Thread.CurrentThread.ManagedThreadId); _backgroundColorOfResult = order.Options.BackgroundColor; XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(order.Document.RawDom); var browser = GetBrowserForPaperSize(order.Document.RawDom); if (browser == null) { return; } if (!CreateThumbNail(order, browser, out pendingThumbnail) && !order.Canceled) { // For some reason...possibly another navigation was in progress...we can't do this just now. // Try it again later. // Enhance: should we have some limit after which we give up? QueueOrder(order); } if (pendingThumbnail == null) { pendingThumbnail = Resources.PagePlaceHolder; } else if (!string.IsNullOrEmpty(order.ThumbNailFilePath)) { try { //gives a blank _pendingThumbnail.Save(thumbNailFilePath); using (Bitmap b = new Bitmap(pendingThumbnail)) { RobustImageIO.SaveImage(b, order.ThumbNailFilePath); } } catch (Exception) { //this is going to fail if we don't have write permission } } pendingThumbnail.Tag = order.ThumbNailFilePath; //usefull if we later know we need to clear out that file Debug.WriteLine("Thumbnail created with dimensions ({0},{1})", browser.Width, browser.Height); try //I saw a case where this threw saying that the key was already in there, even though back at the beginning of this function, it wasn't. { if (_images.ContainsKey(order.Key)) { _images.Remove(order.Key); } _images.Add(order.Key, pendingThumbnail); } catch (Exception error) { Logger.WriteMinorEvent("Skipping minor error: " + error.Message); //not worth crashing over, at this point in Bloom's life, since it's just a cache. But since then, I did add a lock() around all this. } } //order.ResultingThumbnail = pendingThumbnail; if (_disposed) { return; } Logger.WriteMinorEvent("HtmlThumNailer ({1}): finished work on thumbnail ({0})", order.ThumbNailFilePath, Thread.CurrentThread.ManagedThreadId); order.Callback(pendingThumbnail); }
private static void TryGetScreenshot(Control controlForScreenshotting) { if (controlForScreenshotting == null) { Logger.WriteEvent("Bloom was unable to create a screenshot as no active form could be found"); return; } _takingScreenshotLock.Wait(); // Acquire the lock try { SafeInvoke.Invoke("Screen Shot", controlForScreenshotting, false, true, () => { try { // I got tired of landing here in the debugger so I'm avoiding the exception if (controlForScreenshotting.Bounds.Width == 0) { ResetScreenshotFile(); } else { var bounds = controlForScreenshotting.Bounds; var screenshot = new Bitmap(bounds.Width, bounds.Height); using (var g = Graphics.FromImage(screenshot)) { if (controlForScreenshotting.Parent == null) { g.CopyFromScreen(bounds.Left, bounds.Top, 0, 0, bounds.Size); // bounds already in screen coords } else { g.CopyFromScreen( controlForScreenshotting.PointToScreen(new Point(bounds.Left, bounds.Top)), Point.Empty, bounds.Size); } } _reportInfo.ScreenshotTempFile = TempFile.WithFilename(ScreenshotName); RobustImageIO.SaveImage(screenshot, _reportInfo.ScreenshotTempFile.Path, ImageFormat.Png); } } catch (Exception e) { ResetScreenshotFile(); Logger.WriteError("Bloom was unable to create a screenshot.", e); } finally { // Release lock (Unblock others) if (_takingScreenshotLock.CurrentCount == 0) { _takingScreenshotLock.Release(); } } } ); } catch (Exception error) { // Release lock (Unblock others) if (_takingScreenshotLock.CurrentCount == 0) { _takingScreenshotLock.Release(); } Debug.Fail("This error would be swallowed in release version: " + error.Message); SIL.Reporting.Logger.WriteEvent("**** " + error.Message); } }
private void ThumbnailReady(string exportFolder, HtmlDom dom, Image image) { string term; string week; try { term = dom.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim(); week = dom.SelectSingleNode("//div[contains(@data-book,'week')]").InnerText.Trim(); } catch (Exception e) { Debug.Fail("Book missing either term or week variable"); throw new ApplicationException("This page is lacking either a term or week data-book variable."); } //the selector for day one is different because it doesn't have @data-* attribute XmlElement dayNode = dom.SelectSingleNode("//div[contains(@class,'DayStyle')]"); string page = "?"; // many pupil books don't have a specific day per page if (dom.SelectSingleNode("//div[contains(@class,'day5Left')]") != null) // in P2, we have 2 pages for day 5, so we can't use the 'DayStyle' to differentiate them { page = "5"; } else if (dom.SelectSingleNode("//div[contains(@class,'day5Right')]") != null) { page = "6"; } else if (dayNode != null) { page = dayNode.InnerText.Trim(); } else { if (dom.SelectSingleNode("//div[contains(@class,'page1') or contains(@class,'storyPageLeft')]") != null) { page = "1"; } else if (dom.SelectSingleNode("//div[contains(@class,'page2') or contains(@class,'storyPageRight')]") != null) { page = "2"; } else if (dom.SelectSingleNode("//div[contains(@class,'page3') or contains(@class,'thirdPage')]") != null) { page = "3"; } else if (dom.SelectSingleNode("//div[contains(@class,'page4') or contains(@class,'fourthPage')]") != null) { page = "4"; } else { Debug.Fail("Couldn't figure out what page this is."); } } var fileName = Language1Iso639Code + "-t" + term + "-w" + week + "-p" + page + ".png"; //just doing image.Save() works for .bmp and .jpg, but not .png using (var b = new Bitmap(image)) { RobustImageIO.SaveImage(b, Path.Combine(exportFolder, fileName)); } }