示例#1
0
        /// <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);
                }
            }
                                        );
        }
示例#3
0
 /// <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());
            }
        }
示例#5
0
        // 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);
        }
示例#6
0
        /// <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);
            }
        }
示例#7
0
        /// <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);
                }
        }
示例#8
0
        /// <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);
            }
        }
示例#9
0
        /// <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));
        }
示例#10
0
        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);
            }
        }
示例#11
0
        // 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);
        }
示例#12
0
        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);
        }
示例#13
0
        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);
            }
        }
示例#14
0
        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));
            }
        }