packages feed

hakyll-images 1.2.0 → 1.2.1

raw patch · 16 files changed

+1149/−1090 lines, 16 filessetup-changedPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,64 +1,68 @@-# Change log
-
-## Release 1.2.0
-
-* Made `JpgQuality` an opaque constructor, so that we can normalize the input to the range [0, 100].
-
-## Release 1.1.1
-
-* Updated the test suite to support `Hakyll.Core.Runtime.RunMode` (added in hakyll 4.15) (#10).
-
-## Release 1.1.0
-
-* Added support for GIFs (#9).
-* Releases are now automatically uploaded via Github Actions
-
-## Release 1.0.1
-
-* The module `Hakyll.Images.Metadata` is now exposed (#5).
-
-## Release 1.0.0
-
-* Updated dependency bounds.
-
-## Release 0.4.4
-
-* Added the `imageMetadata` compiler, to extract metadata from images.
-
-## Release 0.4.3
-
-* hakyll-images now handles file extensions in a case-insensitive manner (extension of PR #4).
-
-## Release 0.4.2
-
-* Added `ensureFitCompiler`, a Hakyll compiler much like `scaleImageCompiler` but that will only scale images down.
-
-## Release 0.4.1
-
-* Added some regression tests
-* Simplified type architecture (no surface changes)
-
-## Release 0.4
-
-* Fixed an issue from version 0.3.1 where some type instances were missing to write images to disk.
-
-## Release 0.3.1
-
-* Change underlying image type to carry image format around.
-
-## Release 0.3
-
-* Refactored the internal structure to allow for composition of compilers
-
-## Release 0.1.1
-
-* Exposed `resizeImageCompiler` and `scaleImageCompiler` to the base `Hakyll.Images` module
-
-## Release 0.1.0
-
-* added `resizeImageCompiler` to resize images to a specific shape;
-* added `scaleImageCompiler` to scale images while keeping aspect ratio.
-
-## Release 0.0.1
-
-* Added compressJpgCompiler to compress JPEGs.
+# Change log++## Release 1.2.1++* Fixed an issue where compressing certain JPEGs would also rotate them, due to metadata being stripped (#11).++## Release 1.2.0++* Made `JpgQuality` an opaque constructor, so that we can normalize the input to the range [0, 100].++## Release 1.1.1++* Updated the test suite to support `Hakyll.Core.Runtime.RunMode` (added in hakyll 4.15) (#10).++## Release 1.1.0++* Added support for GIFs (#9).+* Releases are now automatically uploaded via Github Actions++## Release 1.0.1++* The module `Hakyll.Images.Metadata` is now exposed (#5).++## Release 1.0.0++* Updated dependency bounds.++## Release 0.4.4++* Added the `imageMetadata` compiler, to extract metadata from images.++## Release 0.4.3++* hakyll-images now handles file extensions in a case-insensitive manner (extension of PR #4).++## Release 0.4.2++* Added `ensureFitCompiler`, a Hakyll compiler much like `scaleImageCompiler` but that will only scale images down.++## Release 0.4.1++* Added some regression tests+* Simplified type architecture (no surface changes)++## Release 0.4++* Fixed an issue from version 0.3.1 where some type instances were missing to write images to disk.++## Release 0.3.1++* Change underlying image type to carry image format around.++## Release 0.3++* Refactored the internal structure to allow for composition of compilers++## Release 0.1.1++* Exposed `resizeImageCompiler` and `scaleImageCompiler` to the base `Hakyll.Images` module++## Release 0.1.0++* added `resizeImageCompiler` to resize images to a specific shape;+* added `scaleImageCompiler` to scale images while keeping aspect ratio.++## Release 0.0.1++* Added compressJpgCompiler to compress JPEGs.
LICENSE.md view
@@ -1,11 +1,11 @@-Copyright 2018-2020 Laurent P. René de Cotret
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright 2018-2020 Laurent P. René de Cotret++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -1,87 +1,87 @@-# hakyll-images
-
-[![Hackage version](https://img.shields.io/hackage/v/hakyll-images.svg)](http://hackage.haskell.org/package/hakyll-images)
-
-## A Haskell package containing utilities to deal with images in the context of Hakyll
-
-[Hakyll](https://hackage.haskell.org/package/hakyll) is a static website compiler library. As one of the benefits of static websites is their small size, this repository aims at providing utilities to work with images in the context of Hakyll. Example usage includes:
-
-* Re-encoding Jpeg images at a lower quality to make them much smaller;
-* Re-sizing images to fit within a certain shape;
-
-## Usage
-
-`hakyll-images` is meant to be integrated within a Hakyll program. Here are some examples within a Hakyll program:
-
-```haskell
-import Hakyll
-import Hakyll.Images        ( loadImage
-                            , compressJpgCompiler
-                            , resizeImageCompiler
-                            , scaleImageCompiler
-                            )
-
-(...)
-
-hakyll $ do
-
-    -- Compress all source Jpegs to a Jpeg quality of 50 (maximum of 100)
-    match "images/**.jpg" $ do
-        route idRoute
-        compile $ loadImage 
-            >>= compressJpgCompiler 50
-
-    -- Resize all profile pictures to 64x48
-    -- Aspect ratio might be mangled
-    match "profiles/**.jpg" $ do
-        route idRoute
-        compile $ loadImage 
-            >>= resizeImageCompiler 64 48
-            >>= compressJpgCompiler 50
-
-    -- Scale images to fit within a 600x400 box
-    -- Aspect ratio will be preserved
-    match "images/**.png" $ do
-        route idRoute
-        compile $ loadImage
-            >>= scaleImageCompiler 600 400
-
-    (...)
-```
-
-Take a look at the [documentation](hackage.haskell.org/package/hakyll-images) for more usage examples.
-
-If you would like a feature added, consider creating [an issue on Github](https://github.com/LaurentRDC/hakyll-images/issues/)
-
-## Installation
-
-### From Hackage
-
-`hakyll-images` is available on [Hackage](https://hackage.haskell.org). Using the [`cabal-install`](https://www.haskell.org/cabal/) tool:
-
-```bash
-cabal update
-cabal install hakyll-images
-```
-
-### From source
-
-Building from source can be done using [`stack`](https://docs.haskellstack.org/en/stable/README/) or [`cabal`](https://www.haskell.org/cabal/):
-
-```bash
-git clone github.com/LaurentRDC/hakyll-images.git
-cd hakyll-images
-stack install # Alternatively, `cabal install`
-```
-
-## Documentation
-
-The documentation for the latest release is available on the [Hackage page](http://hackage.haskell.org/package/hakyll-images/). 
-
-## Support  Issues / Feature requests
-
-All support requests (e.g. installation issues, unclear documentation, bugs, etc.) should of [filed on Github as an issue](https://github.com/LaurentRDC/hakyll-images/issues/)
-
-## License
-
-This package is made available under the BSD 3-clause license. For more details, see the [LICENSE.md](https://github.com/LaurentRDC/hakyll-images/blob/master/LICENSE.md)
+# hakyll-images++[![Hackage version](https://img.shields.io/hackage/v/hakyll-images.svg)](http://hackage.haskell.org/package/hakyll-images)++## A Haskell package containing utilities to deal with images in the context of Hakyll++[Hakyll](https://hackage.haskell.org/package/hakyll) is a static website compiler library. As one of the benefits of static websites is their small size, this repository aims at providing utilities to work with images in the context of Hakyll. Example usage includes:++* Re-encoding Jpeg images at a lower quality to make them much smaller;+* Re-sizing images to fit within a certain shape;++## Usage++`hakyll-images` is meant to be integrated within a Hakyll program. Here are some examples within a Hakyll program:++```haskell+import Hakyll+import Hakyll.Images        ( loadImage+                            , compressJpgCompiler+                            , resizeImageCompiler+                            , scaleImageCompiler+                            )++(...)++hakyll $ do++    -- Compress all source Jpegs to a Jpeg quality of 50 (maximum of 100)+    match "images/**.jpg" $ do+        route idRoute+        compile $ loadImage +            >>= compressJpgCompiler 50++    -- Resize all profile pictures to 64x48+    -- Aspect ratio might be mangled+    match "profiles/**.jpg" $ do+        route idRoute+        compile $ loadImage +            >>= resizeImageCompiler 64 48+            >>= compressJpgCompiler 50++    -- Scale images to fit within a 600x400 box+    -- Aspect ratio will be preserved+    match "images/**.png" $ do+        route idRoute+        compile $ loadImage+            >>= scaleImageCompiler 600 400++    (...)+```++Take a look at the [documentation](hackage.haskell.org/package/hakyll-images) for more usage examples.++If you would like a feature added, consider creating [an issue on Github](https://github.com/LaurentRDC/hakyll-images/issues/)++## Installation++### From Hackage++`hakyll-images` is available on [Hackage](https://hackage.haskell.org). Using the [`cabal-install`](https://www.haskell.org/cabal/) tool:++```bash+cabal update+cabal install hakyll-images+```++### From source++Building from source can be done using [`stack`](https://docs.haskellstack.org/en/stable/README/) or [`cabal`](https://www.haskell.org/cabal/):++```bash+git clone github.com/LaurentRDC/hakyll-images.git+cd hakyll-images+stack install # Alternatively, `cabal install`+```++## Documentation++The documentation for the latest release is available on the [Hackage page](http://hackage.haskell.org/package/hakyll-images/). ++## Support  Issues / Feature requests++All support requests (e.g. installation issues, unclear documentation, bugs, etc.) should of [filed on Github as an issue](https://github.com/LaurentRDC/hakyll-images/issues/)++## License++This package is made available under the BSD 3-clause license. For more details, see the [LICENSE.md](https://github.com/LaurentRDC/hakyll-images/blob/master/LICENSE.md)
Setup.hs view
@@ -1,7 +1,7 @@--- This script is used to build and install your package. Typically you don't
--- need to change it. The Cabal documentation has more information about this
--- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.
-import qualified Distribution.Simple
-
-main :: IO ()
-main = Distribution.Simple.defaultMain
+-- This script is used to build and install your package. Typically you don't+-- need to change it. The Cabal documentation has more information about this+-- file: <https://www.haskell.org/cabal/users-guide/installing-packages.html>.+import qualified Distribution.Simple++main :: IO ()+main = Distribution.Simple.defaultMain
hakyll-images.cabal view
@@ -1,81 +1,82 @@-cabal-version: 2.2
-
-name:           hakyll-images
-version:        1.2.0
-synopsis:       Hakyll utilities to work with images
-description:    hakyll-images is an add-on to the hakyll package. It adds utilities to work with images, including JPEG compression.
-category:       Web
-homepage:       https://github.com/LaurentRDC/hakyll-images#readme
-bug-reports:    https://github.com/LaurentRDC/hakyll-images/issues
-author:         Laurent P. René de Cotret
-maintainer:     Laurent P. René de Cotret
-license:        BSD-3-Clause
-license-file:   LICENSE.md
-build-type:     Simple
-extra-source-files:
-    CHANGELOG.md
-    LICENSE.md
-    README.md
-data-files:
-    tests/data/piccolo.jpg
-    tests/data/donkey.gif
-
-source-repository head
-  type: git
-  location: https://github.com/LaurentRDC/hakyll-images
-
-library
-  exposed-modules:
-      Hakyll.Images
-      Hakyll.Images.CompressJpg
-      Hakyll.Images.Resize
-      Hakyll.Images.Metadata
-  other-modules:
-      Hakyll.Images.Common
-      Paths_hakyll_images
-  autogen-modules:
-      Paths_hakyll_images
-  hs-source-dirs:
-      library
-  ghc-options: -Wall -Wcompat
-  build-depends:
-      JuicyPixels       >= 3   && <4
-    , JuicyPixels-extra >  0.3 && <1
-    , base              >= 4.8 && <5
-    , binary            >= 0.5 && <1
-    , bytestring        >= 0.9 && <1
-    , hakyll            >  4   && <5
-  default-language: Haskell2010
-
-test-suite spec
-  type: exitcode-stdio-1.0
-  main-is: TestSuite.hs
-  other-modules:
-      Hakyll.Images.Common.Tests
-      Hakyll.Images.CompressJpg.Tests
-      Hakyll.Images.Metadata.Tests
-      Hakyll.Images.Resize.Tests
-      Hakyll.Images
-      Hakyll.Images.Common
-      Hakyll.Images.CompressJpg
-      Hakyll.Images.Metadata
-      Hakyll.Images.Resize
-      Paths_hakyll_images
-  hs-source-dirs:
-      tests
-      library
-  ghc-options: -Wall -Wcompat
-  build-depends:
-      HUnit-approx      >= 1    && <2
-    , JuicyPixels       >= 3    && <4
-    , JuicyPixels-extra >  0.3  && <1
-    , base              >= 4.8  && <5
-    , binary            >= 0.5  && <1
-    , bytestring        >= 0.9  && <1
-    , directory         >= 1    && <2
-    , filepath          >= 1    && <2
-    , hakyll            >= 4    && <5
-    , hakyll-images
-    , tasty             >= 0.11 && <2
-    , tasty-hunit       >= 0.9  && <1
-  default-language: Haskell2010
+cabal-version: 2.2++name:           hakyll-images+version:        1.2.1+synopsis:       Hakyll utilities to work with images+description:    hakyll-images is an add-on to the hakyll package. It adds utilities to work with images, including JPEG compression.+category:       Web+homepage:       https://github.com/LaurentRDC/hakyll-images#readme+bug-reports:    https://github.com/LaurentRDC/hakyll-images/issues+author:         Laurent P. René de Cotret+maintainer:     Laurent P. René de Cotret+license:        BSD-3-Clause+license-file:   LICENSE.md+build-type:     Simple+extra-source-files:+    CHANGELOG.md+    LICENSE.md+    README.md+data-files:+    tests/data/piccolo.jpg+    tests/data/donkey.gif+    tests/data/issue11.jpeg++source-repository head+  type: git+  location: https://github.com/LaurentRDC/hakyll-images++library+  exposed-modules:+      Hakyll.Images+      Hakyll.Images.CompressJpg+      Hakyll.Images.Resize+      Hakyll.Images.Metadata+  other-modules:+      Hakyll.Images.Common+      Paths_hakyll_images+  autogen-modules:+      Paths_hakyll_images+  hs-source-dirs:+      library+  ghc-options: -Wall -Wcompat+  build-depends:+      JuicyPixels       >= 3   && <4+    , JuicyPixels-extra >  0.3 && <1+    , base              >= 4.8 && <5+    , binary            >= 0.5 && <1+    , bytestring        >= 0.9 && <1+    , hakyll            >  4   && <5+  default-language: Haskell2010++test-suite spec+  type: exitcode-stdio-1.0+  main-is: TestSuite.hs+  other-modules:+      Hakyll.Images.Common.Tests+      Hakyll.Images.CompressJpg.Tests+      Hakyll.Images.Metadata.Tests+      Hakyll.Images.Resize.Tests+      Hakyll.Images+      Hakyll.Images.Common+      Hakyll.Images.CompressJpg+      Hakyll.Images.Metadata+      Hakyll.Images.Resize+      Paths_hakyll_images+  hs-source-dirs:+      tests+      library+  ghc-options: -Wall -Wcompat+  build-depends:+      HUnit-approx      >= 1    && <2+    , JuicyPixels       >= 3    && <4+    , JuicyPixels-extra >  0.3  && <1+    , base              >= 4.8  && <5+    , binary            >= 0.5  && <1+    , bytestring        >= 0.9  && <1+    , directory         >= 1    && <2+    , filepath          >= 1    && <2+    , hakyll            >= 4    && <5+    , hakyll-images+    , tasty             >= 0.11 && <2+    , tasty-hunit       >= 0.9  && <1+  default-language: Haskell2010
library/Hakyll/Images.hs view
@@ -1,78 +1,78 @@--- |
--- Module      : Hakyll.Images
--- Description : Hakyll utilities for image files
--- Copyright   : (c) Laurent P René de Cotret, 2019 - present
--- License     : BSD3
--- Maintainer  : laurent.decotret@outlook.com
--- Stability   : unstable
--- Portability : portable
---
--- This package defines a few Hakyll compilers. These compilers help deal with images
--- in the context of Hakyll programs, such as JPEG compression or image resizing.
---
--- Items must be loaded before compilers can be used, like so:
---
--- @
---     import Hakyll
---     import Hakyll.Images        ( loadImage
---                                 , resizeImageCompiler
---                                 )
---
---     hakyll $ do
---
---         -- Resize all profile pictures with .png extensions to 64x48
---         match "profiles/**.png" $ do
---             route idRoute
---             compile $ loadImage
---                 >>= resizeImageCompiler 64 48
---
---         (... omitted ...)
--- @
---
--- Compilers can be sequenced easily as well:
---
--- @
---     import Hakyll
---     import Hakyll.Images        ( loadImage
---                                 , compressJpgCompiler
---                                 , scaleImageCompiler
---                                 )
---
---     hakyll $ do
---
---         -- Resize all JPEgs to fit inside of 800x600
---         -- Also compress to a quality of 25/100
---         match "pictures/**.jpg" $ do
---             route idRoute
---             compile $ loadImage
---                 >>= scaleImageCompiler 800 600
---                 >>= compressJpgCompiler 25
---
---         (... omitted ...)
--- @
-module Hakyll.Images
-  ( -- Basic types and functions
-    Image,
-    loadImage,
-    -- Handling metadata
-    module Hakyll.Images.Metadata,
-    -- Jpg compression
-    JpgQuality,
-    compressJpg,
-    compressJpgCompiler,
-    -- Image scaling
-    Width,
-    Height,
-    resize,
-    resizeImageCompiler,
-    scale,
-    scaleImageCompiler,
-    ensureFit,
-    ensureFitCompiler,
-  )
-where
-
-import Hakyll.Images.Common
-import Hakyll.Images.CompressJpg
-import Hakyll.Images.Metadata
-import Hakyll.Images.Resize
+-- |+-- Module      : Hakyll.Images+-- Description : Hakyll utilities for image files+-- Copyright   : (c) Laurent P René de Cotret, 2019 - present+-- License     : BSD3+-- Maintainer  : laurent.decotret@outlook.com+-- Stability   : unstable+-- Portability : portable+--+-- This package defines a few Hakyll compilers. These compilers help deal with images+-- in the context of Hakyll programs, such as JPEG compression or image resizing.+--+-- Items must be loaded before compilers can be used, like so:+--+-- @+--     import Hakyll+--     import Hakyll.Images        ( loadImage+--                                 , resizeImageCompiler+--                                 )+--+--     hakyll $ do+--+--         -- Resize all profile pictures with .png extensions to 64x48+--         match "profiles/**.png" $ do+--             route idRoute+--             compile $ loadImage+--                 >>= resizeImageCompiler 64 48+--+--         (... omitted ...)+-- @+--+-- Compilers can be sequenced easily as well:+--+-- @+--     import Hakyll+--     import Hakyll.Images        ( loadImage+--                                 , compressJpgCompiler+--                                 , scaleImageCompiler+--                                 )+--+--     hakyll $ do+--+--         -- Resize all JPEgs to fit inside of 800x600+--         -- Also compress to a quality of 25/100+--         match "pictures/**.jpg" $ do+--             route idRoute+--             compile $ loadImage+--                 >>= scaleImageCompiler 800 600+--                 >>= compressJpgCompiler 25+--+--         (... omitted ...)+-- @+module Hakyll.Images+  ( -- Basic types and functions+    Image,+    loadImage,+    -- Handling metadata+    module Hakyll.Images.Metadata,+    -- Jpg compression+    JpgQuality,+    compressJpg,+    compressJpgCompiler,+    -- Image scaling+    Width,+    Height,+    resize,+    resizeImageCompiler,+    scale,+    scaleImageCompiler,+    ensureFit,+    ensureFitCompiler,+  )+where++import Hakyll.Images.Common+import Hakyll.Images.CompressJpg+import Hakyll.Images.Metadata+import Hakyll.Images.Resize
library/Hakyll/Images/Common.hs view
@@ -1,101 +1,101 @@-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE FlexibleInstances #-}
--- |
--- Module      : Hakyll.Images.Common
--- Description : Types and utilities for Hakyll.Images
--- Copyright   : (c) Laurent P René de Cotret, 2019 - present
--- License     : BSD3
--- Maintainer  : laurent.decotret@outlook.com
--- Stability   : unstable
--- Portability : portable
-module Hakyll.Images.Common
-  ( Image (..),
-    ImageFormat (..),
-    loadImage,
-    encode,
-  )
-where
-
-import Codec.Picture.Saving
-import Codec.Picture.Types (DynamicImage)
-import Data.Binary (Binary (..))
-import Data.ByteString (ByteString)
-import Data.ByteString.Lazy (toStrict)
-import Data.Char (toLower)
-import Data.Either (fromRight)
-import Data.Typeable (Typeable)
-import GHC.Generics (Generic)
-import Hakyll.Core.Compiler (Compiler, getResourceLBS, getUnderlyingExtension)
-import Hakyll.Core.Item (Item (..))
-import Hakyll.Core.Writable (Writable (..))
-import Prelude hiding (readFile)
-
--- Supported (i.e. encodable) image formats
-data ImageFormat
-  = Jpeg
-  | Png
-  | Bitmap
-  | Tiff
-  | Gif
-  deriving (Eq, Generic)
-
--- Automatic derivation of Binary instances requires Generic
-instance Binary ImageFormat
-
-data Image = Image
-  { format :: ImageFormat,
-    image :: ByteString
-  }
-  deriving (Typeable)
-
--- When writing to disk, we ignore the image format.
--- Trusting users to route correctly.
-instance Writable Image where
-  -- Write the bytestring content
-  write fp item = write fp (image <$> item)
-
--- Binary instance looks similar to the binary instance for a Hakyll Item
-instance Binary Image where
-  put (Image fmt content) = put fmt >> put content
-  get = Image <$> get <*> get
-
--- | Load an image from a file.
--- This function can be combined with other compilers.
---
--- @
--- match "*.jpg" $ do
---     route idRoute
---     compile $ loadImage
---         >>= compressJpgCompiler 50
--- @
-loadImage :: Compiler (Item Image)
-loadImage = do
-  content <- fmap toStrict <$> getResourceLBS
-  fmt <- fromExt <$> getUnderlyingExtension
-  return $ Image fmt <$> content
-
--- | Translation between file extensions and image formats.
--- It is important to keep track of image formats because Hakyll
--- compilers provides raw bytestrings and filenames.
---
--- This function is case-insensitive
-fromExt :: String -> ImageFormat
-fromExt ext = fromExt' $ toLower <$> ext
-  where
-    fromExt' ".jpeg" = Jpeg
-    fromExt' ".jpg" = Jpeg
-    fromExt' ".png" = Png
-    fromExt' ".bmp" = Bitmap
-    fromExt' ".tif" = Tiff
-    fromExt' ".tiff" = Tiff
-    fromExt' ".gif" = Gif
-    fromExt' ext' = error $ "Unsupported format: " <> ext'
-
--- Encode images based on file extension
-encode :: ImageFormat -> DynamicImage -> Image
-encode Jpeg im = Image Jpeg $ (toStrict . imageToJpg 100) im
-encode Png im = Image Png $ (toStrict . imageToPng) im
-encode Bitmap im = Image Bitmap $ (toStrict . imageToBitmap) im
-encode Tiff im = Image Tiff $ (toStrict . imageToTiff) im
-encode Gif im = Image Gif $ (toStrict . fromRight (error "Could not parse gif") . imageToGif) im
+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+-- |+-- Module      : Hakyll.Images.Common+-- Description : Types and utilities for Hakyll.Images+-- Copyright   : (c) Laurent P René de Cotret, 2019 - present+-- License     : BSD3+-- Maintainer  : laurent.decotret@outlook.com+-- Stability   : unstable+-- Portability : portable+module Hakyll.Images.Common+  ( Image (..),+    ImageFormat (..),+    loadImage,+    encode,+  )+where++import Codec.Picture.Saving+import Codec.Picture.Types (DynamicImage)+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString.Lazy (toStrict)+import Data.Char (toLower)+import Data.Either (fromRight)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import Hakyll.Core.Compiler (Compiler, getResourceLBS, getUnderlyingExtension)+import Hakyll.Core.Item (Item (..))+import Hakyll.Core.Writable (Writable (..))+import Prelude hiding (readFile)++-- Supported (i.e. encodable) image formats+data ImageFormat+  = Jpeg+  | Png+  | Bitmap+  | Tiff+  | Gif+  deriving (Eq, Generic)++-- Automatic derivation of Binary instances requires Generic+instance Binary ImageFormat++data Image = Image+  { format :: ImageFormat,+    image :: ByteString+  }+  deriving (Typeable)++-- When writing to disk, we ignore the image format.+-- Trusting users to route correctly.+instance Writable Image where+  -- Write the bytestring content+  write fp item = write fp (image <$> item)++-- Binary instance looks similar to the binary instance for a Hakyll Item+instance Binary Image where+  put (Image fmt content) = put fmt >> put content+  get = Image <$> get <*> get++-- | Load an image from a file.+-- This function can be combined with other compilers.+--+-- @+-- match "*.jpg" $ do+--     route idRoute+--     compile $ loadImage+--         >>= compressJpgCompiler 50+-- @+loadImage :: Compiler (Item Image)+loadImage = do+  content <- fmap toStrict <$> getResourceLBS+  fmt <- fromExt <$> getUnderlyingExtension+  return $ Image fmt <$> content++-- | Translation between file extensions and image formats.+-- It is important to keep track of image formats because Hakyll+-- compilers provides raw bytestrings and filenames.+--+-- This function is case-insensitive+fromExt :: String -> ImageFormat+fromExt ext = fromExt' $ toLower <$> ext+  where+    fromExt' ".jpeg" = Jpeg+    fromExt' ".jpg" = Jpeg+    fromExt' ".png" = Png+    fromExt' ".bmp" = Bitmap+    fromExt' ".tif" = Tiff+    fromExt' ".tiff" = Tiff+    fromExt' ".gif" = Gif+    fromExt' ext' = error $ "Unsupported format: " <> ext'++-- Encode images based on file extension+encode :: ImageFormat -> DynamicImage -> Image+encode Jpeg im = Image Jpeg $ (toStrict . imageToJpg 100) im+encode Png im = Image Png $ (toStrict . imageToPng) im+encode Bitmap im = Image Bitmap $ (toStrict . imageToBitmap) im+encode Tiff im = Image Tiff $ (toStrict . imageToTiff) im+encode Gif im = Image Gif $ (toStrict . fromRight (error "Could not parse gif") . imageToGif) im
library/Hakyll/Images/CompressJpg.hs view
@@ -1,95 +1,135 @@-{-# LANGUAGE GeneralisedNewtypeDeriving #-}
--- |
--- Module      : Hakyll.Images.CompressJpg
--- Description : Hakyll compiler to compress Jpeg images
--- Copyright   : (c) Laurent P René de Cotret, 2019 - present
--- License     : BSD3
--- Maintainer  : laurent.decotret@outlook.com
--- Stability   : unstable
--- Portability : portable
---
--- This module defines a Hakyll compiler, 'compressJpgCompiler', which can be used to
--- re-encode Jpeg images at a lower quality during website compilation. Original images are
--- left unchanged, but compressed images can be up to 10x smaller.
---
--- The @compressJpgCompiler@ is expected to be used like this:
---
--- @
---     import Hakyll
---     import Hakyll.Images        ( loadImage
---                                 , compressJpgCompiler
---                                 )
---
---     hakyll $ do
---
---         -- Compress all source Jpegs to a Jpeg quality of 50
---         match "images/**.jpg" $ do
---             route idRoute
---             compile $ loadImage
---                 >>= compressJpgCompiler 50
---
---         (... omitted ...)
--- @
-module Hakyll.Images.CompressJpg
-  ( JpgQuality,
-    compressJpgCompiler,
-    compressJpg,
-  )
-where
-
-import Codec.Picture.Jpg (decodeJpeg)
-import Codec.Picture.Saving (imageToJpg)
-import Data.ByteString.Lazy (toStrict)
-import Hakyll.Core.Compiler (Compiler)
-import Hakyll.Core.Item (Item (..))
-import Hakyll.Images.Common
-  ( Image (..),
-    ImageFormat (..),
-    format,
-    image,
-  )
-import Numeric.Natural (Natural)
-
-
--- | Jpeg encoding quality, from 0 (lower quality) to 100 (best quality).
--- @since 1.2.0
-newtype JpgQuality = JpgQuality Natural
-  deriving (Num, Eq, Enum, Ord, Real, Integral)
-
-
--- | @JpgQuality@ smart constructor. Ensures that @JpgQuality@ is always
--- in the interval [0, 100]. Numbers outside this range will result in either
--- a quality of 0 or 100.
---
--- @since 1.2.0
-mkJpgQuality :: Integral a => a -> JpgQuality
-mkJpgQuality q | q < 0     = JpgQuality 0
-               | q > 100   = JpgQuality 100
-               | otherwise = JpgQuality (fromIntegral q)
-
-
--- | Compress a JPG bytestring to a certain quality setting.
--- The quality should be between 0 (lowest quality) and 100 (best quality).
--- An error is raised if the image cannot be decoded.
-compressJpg :: Integral a => a -> Image -> Image
-compressJpg quality' src =
-  if format src /= Jpeg
-    then error "Image is not a JPEG."
-    else case decodeJpeg $ image src of
-      Left _ -> error "Loading the image failed."
-      Right dynImage -> Image Jpeg $ toStrict (imageToJpg (fromIntegral quality) dynImage)
-  where quality = mkJpgQuality quality'
-
--- | Compiler that compresses a JPG image to a certain quality setting.
--- The quality should be between 0 (lowest quality) and 100 (best quality).
--- Values outside of this range will be normalized to the interval [0, 100].
--- An error is raised if the image cannot be decoded.
---
--- @
--- match "*.jpg" $ do
---     route idRoute
---     compile $ loadImage
---         >>= compressJpgCompiler 50
--- @
-compressJpgCompiler :: Integral a => a -> Item Image -> Compiler (Item Image)
-compressJpgCompiler quality = return . fmap (compressJpg quality)
+{-# LANGUAGE GeneralisedNewtypeDeriving #-}+{-# LANGUAGE RecordWildCards            #-}+{-# LANGUAGE ScopedTypeVariables        #-}+-- |+-- Module      : Hakyll.Images.CompressJpg+-- Description : Hakyll compiler to compress Jpeg images+-- Copyright   : (c) Laurent P René de Cotret, 2019 - present+-- License     : BSD3+-- Maintainer  : laurent.decotret@outlook.com+-- Stability   : unstable+-- Portability : portable+--+-- This module defines a Hakyll compiler, 'compressJpgCompiler', which can be used to+-- re-encode Jpeg images at a lower quality during website compilation. Original images are+-- left unchanged, but compressed images can be up to 10x smaller.+--+-- The @compressJpgCompiler@ is expected to be used like this:+--+-- @+--     import Hakyll+--     import Hakyll.Images        ( loadImage+--                                 , compressJpgCompiler+--                                 )+--+--     hakyll $ do+--+--         -- Compress all source Jpegs to a Jpeg quality of 50+--         match "images/**.jpg" $ do+--             route idRoute+--             compile $ loadImage+--                 >>= compressJpgCompiler 50+--+--         (... omitted ...)+-- @+module Hakyll.Images.CompressJpg+  ( JpgQuality,+    compressJpgCompiler,+    compressJpg,+  )+where++import Codec.Picture.Types (DynamicImage(..), dropTransparency, pixelMap)+import qualified Codec.Picture.Types as Picture+import Codec.Picture.Metadata (Metadatas, SourceFormat(SourceJpeg), basicMetadata) +import qualified Codec.Picture.Metadata as Meta+import Codec.Picture.Metadata.Exif (ExifTag(TagOrientation))+import Codec.Picture.Jpg (JpgEncodable, decodeJpegWithMetadata, encodeDirectJpegAtQualityWithMetadata)+import Data.ByteString.Lazy (toStrict)+import Data.ByteString (ByteString) +import Hakyll.Core.Compiler (Compiler)+import Hakyll.Core.Item (Item (..))+import Hakyll.Images.Common+  ( Image (..),+    ImageFormat (..),+    format,+    image,+  )+import Numeric.Natural (Natural)+++-- | Jpeg encoding quality, from 0 (lower quality) to 100 (best quality).+-- @since 1.2.0+newtype JpgQuality = JpgQuality Natural+  deriving (Num, Eq, Enum, Ord, Real, Integral)+++-- | @JpgQuality@ smart constructor. Ensures that @JpgQuality@ is always+-- in the interval [0, 100]. Numbers outside this range will result in either+-- a quality of 0 or 100.+--+-- @since 1.2.0+mkJpgQuality :: Integral a => a -> JpgQuality+mkJpgQuality q | q < 0     = JpgQuality 0+               | q > 100   = JpgQuality 100+               | otherwise = JpgQuality (fromIntegral q)+++-- | Compress a JPG bytestring to a certain quality setting.+-- The quality should be between 0 (lowest quality) and 100 (best quality).+-- An error is raised if the image cannot be decoded.+--+-- In the rare case where the JPEG data contains transparency information, it will be dropped.+compressJpg :: Integral a => a -> Image -> Image+compressJpg quality' src =+  if format src /= Jpeg+    then error "Image is not a JPEG."+    -- It is important to preserve some metadata, such as orientation (issue #11).+    else case decodeJpegWithMetadata $ image src of+      Left msg -> error $ "Loading the image failed for the following reason: " <> msg+      Right (dynImage, meta) -> +         Image Jpeg $ case dynImage of +          (ImageY8 img)     -> (encodeJpeg quality meta img)+          (ImageCMYK8 img)  -> (encodeJpeg quality meta img)+          (ImageRGB8 img)   -> (encodeJpeg quality meta img)+          (ImageYCbCr8 img) -> (encodeJpeg quality meta img)+          -- Out of the 5 possible image types that can be returned by `decodeJpegWithMetadata`, only 1+          -- has transparency. This is also the only image type which cannot be re-encoded directly;+          -- we need to remove transparency.+          (ImageYA8 img)    -> (encodeJpeg quality meta (pixelMap dropTransparency img))+          _ -> error "Loading the image failed because the color space is unknown." +  where +    quality = mkJpgQuality quality'+++-- | Encode a JPEG image at a particular quality, preserving some metadata.+encodeJpeg :: (Integral q, JpgEncodable px) +           => q +           -> Metadatas +           -> Picture.Image px +           -> ByteString+encodeJpeg qual meta img@Picture.Image{..} +  = toStrict $ encodeDirectJpegAtQualityWithMetadata (fromIntegral qual) newmeta img+  where+    -- We want to preserve Exif orientation metadata, which is important for presentation (see #11)+    -- However, other metadata tags are notoriously finicky and can leads to corrupted+    -- files. +    exifOrientationMeta = foldMap (\(k, v) -> Meta.singleton (Meta.Exif k) v) +                        $ filter (\(k, _) -> k == TagOrientation) +                        $ Meta.extractExifMetas meta+    newmeta = exifOrientationMeta <> basicMetadata SourceJpeg imageWidth imageHeight+++-- | Compiler that compresses a JPG image to a certain quality setting.+-- The quality should be between 0 (lowest quality) and 100 (best quality).+-- Values outside of this range will be normalized to the interval [0, 100].+-- An error is raised if the image cannot be decoded.+--+-- @+-- match "*.jpg" $ do+--     route idRoute+--     compile $ loadImage+--         >>= compressJpgCompiler 50+-- @+compressJpgCompiler :: Integral a => a -> Item Image -> Compiler (Item Image)+compressJpgCompiler quality = return . fmap (compressJpg quality)
library/Hakyll/Images/Metadata.hs view
@@ -1,41 +1,41 @@--- |
--- Module      : Hakyll.Images.Metadata
--- Description : Handling image metadata
--- Copyright   : (c) Laurent P René de Cotret, 2019 - present
--- License     : BSD3
--- Maintainer  : laurent.decotret@outlook.com
--- Stability   : unstable
--- Portability : portable
-module Hakyll.Images.Metadata
-  ( module Codec.Picture.Metadata,
-    imageMetadata,
-    metadata,
-  )
-where
-
-import Codec.Picture (decodeImageWithMetadata)
-import Codec.Picture.Metadata
-import Hakyll.Core.Compiler (Compiler)
-import Hakyll.Core.Item (Item, itemBody)
-import Hakyll.Images.Common (Image (..))
-
--- | Extract metadata from an image. This function will throw an
--- error in case of a problem.
---
--- This function is for testing purposes.
-metadata :: Image -> Metadatas
-metadata im = either error snd (decodeImageWithMetadata (image im))
-
--- | Extract metadata from an image.
---
--- @
--- match "*.jpg" $ do
---     route idRoute
---     compile $ do
---         im <- loadImage
---         meta <- imageMetadata
---         doSomethingWithMetadata meta
---         ...
--- @
-imageMetadata :: Item Image -> Compiler Metadatas
-imageMetadata = return . metadata . itemBody
+-- |+-- Module      : Hakyll.Images.Metadata+-- Description : Handling image metadata+-- Copyright   : (c) Laurent P René de Cotret, 2019 - present+-- License     : BSD3+-- Maintainer  : laurent.decotret@outlook.com+-- Stability   : unstable+-- Portability : portable+module Hakyll.Images.Metadata+  ( module Codec.Picture.Metadata,+    imageMetadata,+    metadata,+  )+where++import Codec.Picture (decodeImageWithMetadata)+import Codec.Picture.Metadata+import Hakyll.Core.Compiler (Compiler)+import Hakyll.Core.Item (Item, itemBody)+import Hakyll.Images.Common (Image (..))++-- | Extract metadata from an image. This function will throw an+-- error in case of a problem.+--+-- This function is for testing purposes.+metadata :: Image -> Metadatas+metadata im = either error snd (decodeImageWithMetadata (image im))++-- | Extract metadata from an image.+--+-- @+-- match "*.jpg" $ do+--     route idRoute+--     compile $ do+--         im <- loadImage+--         meta <- imageMetadata+--         doSomethingWithMetadata meta+--         ...+-- @+imageMetadata :: Item Image -> Compiler Metadatas+imageMetadata = return . metadata . itemBody
library/Hakyll/Images/Resize.hs view
@@ -1,176 +1,176 @@--- |
--- Module      : Hakyll.Images.Resize
--- Description : Hakyll compiler to resize images
--- Copyright   : (c) Laurent P René de Cotret, 2019 - present
--- License     : BSD3
--- Maintainer  : laurent.decotret@outlook.com
--- Stability   : unstable
--- Portability : portable
---
--- This module defines two Hakyll compilers. The first one, 'resizeImageCompiler',
--- is used to resize images to specific dimensions. The aspect ratio might not be the same.
---
--- The other compiler, `scaleImageCompiler`, scales images to fit within a specified
--- box while preserving aspect ratio.
---
--- @
---     import Hakyll
---     import Hakyll.Images        ( loadImage
---                                 , resizeImageCompiler
---                                 , scaleImageCompiler
---                                 )
---
---     hakyll $ do
---
---         -- Resize all profile pictures with .png extensions to 64x48
---         match "profiles/**.png" $ do
---             route idRoute
---             compile $ loadImage
---                 >>= resizeImageCompiler 64 48
---
---         -- Scale images to fit within a 600x400 box
---         match "images/**" $ do
---             route idRoute
---             compile $ loadImage
---                 >>= scaleImageCompiler 600 400
---
---         (... omitted ...)
--- @
-module Hakyll.Images.Resize
-  ( Width,
-    Height,
-    resize,
-    resizeImageCompiler,
-    scale,
-    scaleImageCompiler,
-    ensureFit,
-    ensureFitCompiler,
-  )
-where
-
-import Codec.Picture (convertRGBA8, decodeImage)
-import Codec.Picture.Extra (scaleBilinear)
-import Codec.Picture.Types (DynamicImage (..), imageHeight, imageWidth)
-import Data.ByteString (ByteString)
-import Data.Ratio ((%))
-import Hakyll.Core.Compiler (Compiler)
-import Hakyll.Core.Item (Item (..))
-import Hakyll.Images.Common (Image (..), encode)
-
-type Width = Int
-
-type Height = Int
-
-decodeImage' :: ByteString -> DynamicImage
-decodeImage' im = case decodeImage im of
-  Left msg -> error msg
-  Right im' -> im'
-
--- | Resize an image to specified width and height using the bilinear transform.
--- The aspect ratio may not be respected.
---
--- In the process, an image is converted to RGBA8. Therefore, some information
--- loss may occur.
-resize :: Width -> Height -> DynamicImage -> DynamicImage
-resize w h = ImageRGBA8 . scaleBilinear w h . convertRGBA8
-
--- | Scale an image to a size that will fit in the specified width and height,
--- while preserving aspect ratio. Images might be scaled up as well.
---
--- In the process, an image is converted to RGBA8. Therefore, some information
--- loss may occur.
---
--- To scale images down only, take a look at 'ensureFit'.
-scale :: Width -> Height -> DynamicImage -> DynamicImage
-scale w h = scale' w h True
-
--- | Scale an image to a size that will fit in the specified width and height,
--- while preserving aspect ratio. Images might be scaled up as well.
---
--- In the process, an image is converted to RGBA8. Therefore, some information
--- loss may occur.
-scale' ::
-  -- | Desired width.
-  Width ->
-  -- | Desired height.
-  Height ->
-  -- | Allow scaling up as well.
-  Bool ->
-  -- | Source image
-  DynamicImage ->
-  -- | Scaled image.
-  DynamicImage
-scale' w h upAllowed img = resize maxWidth maxHeight img
-  where
-    img' = convertRGBA8 img -- Required to extract height and width
-    (imgWidth, imgHeight) = (imageWidth img', imageHeight img')
-    -- Find the smallest resizing that will accomodate both the width
-    -- and height.
-    -- If we don't allow scaling up, minimum scaling is 1
-    resizing =
-      if upAllowed
-        then min (w % imgWidth) (h % imgHeight)
-        else minimum [w % imgWidth, h % imgHeight, 1]
-    maxWidth = round (resizing * fromIntegral imgWidth)
-    maxHeight = round (resizing * fromIntegral imgHeight)
-
--- | Scale an image down to a size that will fit in the specified width and height,
--- while preserving aspect ratio.
---
--- In the process, an image is converted to RGBA8. Therefore, some information
--- loss may occur.
---
--- To scale images up __or__ down, take a look at 'scale'.
-ensureFit :: Width -> Height -> DynamicImage -> DynamicImage
-ensureFit w h = scale' w h False
-
--- | Compiler that resizes images to a specific dimensions. Aspect ratio
--- may not be preserved.
---
--- @
--- match "*.png" $ do
---     route idRoute
---     compile $ loadImage
---         >>= resizeImageCompiler 48 64
--- @
---
--- Note that in the resizing process, images will be converted to RGBA8.
--- To preserve aspect ratio, take a look at 'scaleImageCompiler'.
-resizeImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
-resizeImageCompiler w h item =
-  let fmt = (format . itemBody) item
-   in return $ encode fmt . resize w h . decodeImage' . image <$> item
-
--- | Compiler that rescales images to fit within dimensions. Aspect ratio
--- will be preserved. Images might be scaled up as well.
---
--- @
--- match "*.tiff" $ do
---     route idRoute
---     compile $ loadImage
---         >>= scaleImageCompiler 48 64
--- @
---
--- Note that in the resizing process, images will be converted to RGBA8.
--- To ensure images are only scaled __down__, take a look at 'ensureFitCompiler'.
-scaleImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
-scaleImageCompiler w h item =
-  let fmt = (format . itemBody) item
-   in return $ encode fmt . scale w h . decodeImage' . image <$> item
-
--- | Compiler that ensures images will fit within dimensions. Images might
--- be scaled down, but never up.  Aspect ratio will be preserved.
---
--- @
--- match "*.tiff" $ do
---     route idRoute
---     compile $ loadImage
---         >>= ensureFitCompiler 48 64
--- @
---
--- Note that in the resizing process, images will be converted to RGBA8.
--- To allow the possibility of scaling up, take a look at 'scaleImageCompiler'.
-ensureFitCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)
-ensureFitCompiler w h item =
-  let fmt = (format . itemBody) item
-   in return $ encode fmt . ensureFit w h . decodeImage' . image <$> item
+-- |+-- Module      : Hakyll.Images.Resize+-- Description : Hakyll compiler to resize images+-- Copyright   : (c) Laurent P René de Cotret, 2019 - present+-- License     : BSD3+-- Maintainer  : laurent.decotret@outlook.com+-- Stability   : unstable+-- Portability : portable+--+-- This module defines two Hakyll compilers. The first one, 'resizeImageCompiler',+-- is used to resize images to specific dimensions. The aspect ratio might not be the same.+--+-- The other compiler, `scaleImageCompiler`, scales images to fit within a specified+-- box while preserving aspect ratio.+--+-- @+--     import Hakyll+--     import Hakyll.Images        ( loadImage+--                                 , resizeImageCompiler+--                                 , scaleImageCompiler+--                                 )+--+--     hakyll $ do+--+--         -- Resize all profile pictures with .png extensions to 64x48+--         match "profiles/**.png" $ do+--             route idRoute+--             compile $ loadImage+--                 >>= resizeImageCompiler 64 48+--+--         -- Scale images to fit within a 600x400 box+--         match "images/**" $ do+--             route idRoute+--             compile $ loadImage+--                 >>= scaleImageCompiler 600 400+--+--         (... omitted ...)+-- @+module Hakyll.Images.Resize+  ( Width,+    Height,+    resize,+    resizeImageCompiler,+    scale,+    scaleImageCompiler,+    ensureFit,+    ensureFitCompiler,+  )+where++import Codec.Picture (convertRGBA8, decodeImage)+import Codec.Picture.Extra (scaleBilinear)+import Codec.Picture.Types (DynamicImage (..), imageHeight, imageWidth)+import Data.ByteString (ByteString)+import Data.Ratio ((%))+import Hakyll.Core.Compiler (Compiler)+import Hakyll.Core.Item (Item (..))+import Hakyll.Images.Common (Image (..), encode)++type Width = Int++type Height = Int++decodeImage' :: ByteString -> DynamicImage+decodeImage' im = case decodeImage im of+  Left msg -> error msg+  Right im' -> im'++-- | Resize an image to specified width and height using the bilinear transform.+-- The aspect ratio may not be respected.+--+-- In the process, an image is converted to RGBA8. Therefore, some information+-- loss may occur.+resize :: Width -> Height -> DynamicImage -> DynamicImage+resize w h = ImageRGBA8 . scaleBilinear w h . convertRGBA8++-- | Scale an image to a size that will fit in the specified width and height,+-- while preserving aspect ratio. Images might be scaled up as well.+--+-- In the process, an image is converted to RGBA8. Therefore, some information+-- loss may occur.+--+-- To scale images down only, take a look at 'ensureFit'.+scale :: Width -> Height -> DynamicImage -> DynamicImage+scale w h = scale' w h True++-- | Scale an image to a size that will fit in the specified width and height,+-- while preserving aspect ratio. Images might be scaled up as well.+--+-- In the process, an image is converted to RGBA8. Therefore, some information+-- loss may occur.+scale' ::+  -- | Desired width.+  Width ->+  -- | Desired height.+  Height ->+  -- | Allow scaling up as well.+  Bool ->+  -- | Source image+  DynamicImage ->+  -- | Scaled image.+  DynamicImage+scale' w h upAllowed img = resize maxWidth maxHeight img+  where+    img' = convertRGBA8 img -- Required to extract height and width+    (imgWidth, imgHeight) = (imageWidth img', imageHeight img')+    -- Find the smallest resizing that will accomodate both the width+    -- and height.+    -- If we don't allow scaling up, minimum scaling is 1+    resizing =+      if upAllowed+        then min (w % imgWidth) (h % imgHeight)+        else minimum [w % imgWidth, h % imgHeight, 1]+    maxWidth = round (resizing * fromIntegral imgWidth)+    maxHeight = round (resizing * fromIntegral imgHeight)++-- | Scale an image down to a size that will fit in the specified width and height,+-- while preserving aspect ratio.+--+-- In the process, an image is converted to RGBA8. Therefore, some information+-- loss may occur.+--+-- To scale images up __or__ down, take a look at 'scale'.+ensureFit :: Width -> Height -> DynamicImage -> DynamicImage+ensureFit w h = scale' w h False++-- | Compiler that resizes images to a specific dimensions. Aspect ratio+-- may not be preserved.+--+-- @+-- match "*.png" $ do+--     route idRoute+--     compile $ loadImage+--         >>= resizeImageCompiler 48 64+-- @+--+-- Note that in the resizing process, images will be converted to RGBA8.+-- To preserve aspect ratio, take a look at 'scaleImageCompiler'.+resizeImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)+resizeImageCompiler w h item =+  let fmt = (format . itemBody) item+   in return $ encode fmt . resize w h . decodeImage' . image <$> item++-- | Compiler that rescales images to fit within dimensions. Aspect ratio+-- will be preserved. Images might be scaled up as well.+--+-- @+-- match "*.tiff" $ do+--     route idRoute+--     compile $ loadImage+--         >>= scaleImageCompiler 48 64+-- @+--+-- Note that in the resizing process, images will be converted to RGBA8.+-- To ensure images are only scaled __down__, take a look at 'ensureFitCompiler'.+scaleImageCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)+scaleImageCompiler w h item =+  let fmt = (format . itemBody) item+   in return $ encode fmt . scale w h . decodeImage' . image <$> item++-- | Compiler that ensures images will fit within dimensions. Images might+-- be scaled down, but never up.  Aspect ratio will be preserved.+--+-- @+-- match "*.tiff" $ do+--     route idRoute+--     compile $ loadImage+--         >>= ensureFitCompiler 48 64+-- @+--+-- Note that in the resizing process, images will be converted to RGBA8.+-- To allow the possibility of scaling up, take a look at 'scaleImageCompiler'.+ensureFitCompiler :: Width -> Height -> Item Image -> Compiler (Item Image)+ensureFitCompiler w h item =+  let fmt = (format . itemBody) item+   in return $ encode fmt . ensureFit w h . decodeImage' . image <$> item
tests/Hakyll/Images/Common/Tests.hs view
@@ -1,78 +1,78 @@-{-# LANGUAGE CPP               #-}
-{-# LANGUAGE OverloadedStrings #-}
-
---------------------------------------------------------------------------------
-module Hakyll.Images.Common.Tests
-  ( tests,
-  )
-where
-
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-import Hakyll
-import qualified Hakyll.Core.Logger as L
-import Hakyll.Core.Rules.Internal (RuleSet)
-import Hakyll.Core.Runtime
-import Hakyll.Images
-import System.Directory (doesFileExist)
-import System.Exit      (ExitCode)
-import System.FilePath ((</>))
-import Test.Tasty (TestTree, testGroup)
-import Test.Tasty.HUnit (Assertion, assertBool, testCase)
-import Text.Printf (printf)
-
--- RunMode was introduced in Hakyll 4.15
-runCompat :: Configuration -> L.Logger -> Rules a -> IO (ExitCode, RuleSet)
-runCompat = 
-#if MIN_VERSION_hakyll(4,15,0)
-  run RunModeNormal 
-#else
-  run
-#endif
-
-fromAssertions ::
-  -- | Name
-  String ->
-  -- | Cases
-  [Assertion] ->
-  -- | Result tests
-  [TestTree]
-fromAssertions name =
-  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]
-
-testConfiguration :: Configuration
-testConfiguration =
-  defaultConfiguration
-    { destinationDirectory = "_testsite",
-      storeDirectory = "_teststore",
-      tmpDirectory = "_testtmp",
-      providerDirectory = "tests/data"
-    }
-
-cleanTestEnv :: IO ()
-cleanTestEnv = do
-  removeDirectory $ destinationDirectory testConfiguration
-  removeDirectory $ storeDirectory testConfiguration
-  removeDirectory $ tmpDirectory testConfiguration
-
-case1 :: Assertion
-case1 = do
-  logger <- L.new L.Error
-  _ <- runCompat testConfiguration logger $ do
-    match "*.jpg" $ do
-      route idRoute
-      compile $
-        loadImage
-          >>= compressJpgCompiler (50::Int)
-
-  _ <-
-    assertBool "Image was not written"
-      <$> doesFileExist ( destinationDirectory testConfiguration </> "piccolo.jpg")
-
-  cleanTestEnv
-
---------------------------------------------------------------------------------
-tests :: TestTree
-tests =
-  testGroup "Hakyll.Images.Common.Tests" $ fromAssertions "run" [case1]
+{-# LANGUAGE CPP               #-}+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+module Hakyll.Images.Common.Tests+  ( tests,+  )+where++--------------------------------------------------------------------------------++--------------------------------------------------------------------------------+import Hakyll+import qualified Hakyll.Core.Logger as L+import Hakyll.Core.Rules.Internal (RuleSet)+import Hakyll.Core.Runtime+import Hakyll.Images+import System.Directory (doesFileExist)+import System.Exit      (ExitCode)+import System.FilePath ((</>))+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (Assertion, assertBool, testCase)+import Text.Printf (printf)++-- RunMode was introduced in Hakyll 4.15+runCompat :: Configuration -> L.Logger -> Rules a -> IO (ExitCode, RuleSet)+runCompat = +#if MIN_VERSION_hakyll(4,15,0)+  run RunModeNormal +#else+  run+#endif++fromAssertions ::+  -- | Name+  String ->+  -- | Cases+  [Assertion] ->+  -- | Result tests+  [TestTree]+fromAssertions name =+  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]++testConfiguration :: Configuration+testConfiguration =+  defaultConfiguration+    { destinationDirectory = "_testsite",+      storeDirectory = "_teststore",+      tmpDirectory = "_testtmp",+      providerDirectory = "tests/data"+    }++cleanTestEnv :: IO ()+cleanTestEnv = do+  removeDirectory $ destinationDirectory testConfiguration+  removeDirectory $ storeDirectory testConfiguration+  removeDirectory $ tmpDirectory testConfiguration++case1 :: Assertion+case1 = do+  logger <- L.new L.Error+  _ <- runCompat testConfiguration logger $ do+    match "*.jpg" $ do+      route idRoute+      compile $+        loadImage+          >>= compressJpgCompiler (50::Int)++  _ <-+    assertBool "Image was not written"+      <$> doesFileExist ( destinationDirectory testConfiguration </> "piccolo.jpg")++  cleanTestEnv++--------------------------------------------------------------------------------+tests :: TestTree+tests =+  testGroup "Hakyll.Images.Common.Tests" $ fromAssertions "run" [case1]
tests/Hakyll/Images/CompressJpg/Tests.hs view
@@ -1,70 +1,84 @@---------------------------------------------------------------------------------
-module Hakyll.Images.CompressJpg.Tests
-  ( tests,
-  )
-where
-
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-
-import qualified Data.ByteString as B
-import Hakyll.Images.Common ( Image(Image, image), ImageFormat(Jpeg) )
-import Hakyll.Images.CompressJpg ( compressJpg )
-import Test.Tasty (TestTree, testGroup)
-import Test.Tasty.HUnit (Assertion, assertBool, testCase)
-import Text.Printf (printf)
-
-testJpg :: IO Image
-testJpg = Image Jpeg <$> B.readFile "tests/data/piccolo.jpg"
-
-fromAssertions ::
-  -- | Name
-  String ->
-  -- | Cases
-  [Assertion] ->
-  -- | Result tests
-  [TestTree]
-fromAssertions name =
-  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]
-
--- Test that the standard Image compressed to quality 25/100 is smaller
--- than the initial image
-testCompressionFromImage :: Assertion
-testCompressionFromImage = do
-  im <- testJpg
-  let initialSize = (B.length . image) im
-      finalSize = (B.length . image . compressJpg (25::Int)) im
-
-  assertBool "Image was not compressed" (initialSize > finalSize)
-
--- Test that specifying a JPG encoding below 0 will not fail
-testJpgEncodingOutOfLowerBound :: Assertion
-testJpgEncodingOutOfLowerBound = do
-  im <- testJpg
-  
-  let compressedSize = (B.length . image . compressJpg (-10 :: Int)) im
-      expectedSize = (B.length . image . compressJpg (0 :: Int)) im
-
-  assertBool "Out-of-boumds JpgQuality was not handled properly" (expectedSize == compressedSize)
-
--- Test that specifying a JPG encoding above 100 will fail
-testJpgEncodingOutOfUpperBound :: Assertion
-testJpgEncodingOutOfUpperBound = do
-  im <- testJpg
-  
-  let compressedSize = (B.length . image . compressJpg (150 :: Int)) im
-      expectedSize = (B.length . image . compressJpg (100 :: Int)) im
-
-  assertBool "Out-of-boumds JpgQuality was not handled properly" (expectedSize == compressedSize)
-
---------------------------------------------------------------------------------
-tests :: TestTree
-tests =
-  testGroup "Hakyll.Images.CompressJpg.Tests" $
-    fromAssertions
-        "compressJpg"
-        [ testCompressionFromImage,
-          testJpgEncodingOutOfLowerBound,
-          testJpgEncodingOutOfUpperBound
-        ]
+--------------------------------------------------------------------------------+module Hakyll.Images.CompressJpg.Tests+  ( tests,+  )+where++--------------------------------------------------------------------------------++--------------------------------------------------------------------------------++import Codec.Picture (dynamicMap, imageWidth, imageHeight, imageHeight)+import Codec.Picture.Jpg (decodeJpeg)+import qualified Data.ByteString as B+import Hakyll.Images.Common ( Image(Image, image), ImageFormat(Jpeg) )+import Hakyll.Images.CompressJpg ( compressJpg )+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase)+import Text.Printf (printf)++testJpg :: IO Image+testJpg = Image Jpeg <$> B.readFile "tests/data/piccolo.jpg"++fromAssertions ::+  -- | Name+  String ->+  -- | Cases+  [Assertion] ->+  -- | Result tests+  [TestTree]+fromAssertions name =+  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]++-- Test that the standard Image compressed to quality 25/100 is smaller+-- than the initial image+testCompressionFromImage :: Assertion+testCompressionFromImage = do+  im <- testJpg+  let initialSize = (B.length . image) im+      finalSize = (B.length . image . compressJpg (25::Int)) im++  assertBool "Image was not compressed" (initialSize > finalSize)++-- Test that specifying a JPG encoding below 0 will not fail+testJpgEncodingOutOfLowerBound :: Assertion+testJpgEncodingOutOfLowerBound = do+  im <- testJpg+  +  let compressedSize = (B.length . image . compressJpg (-10 :: Int)) im+      expectedSize = (B.length . image . compressJpg (0 :: Int)) im++  assertBool "Out-of-bounds JpgQuality was not handled properly" (expectedSize == compressedSize)++-- Test that specifying a JPG encoding above 100 will fail+testJpgEncodingOutOfUpperBound :: Assertion+testJpgEncodingOutOfUpperBound = do+  im <- testJpg+  +  let compressedSize = (B.length . image . compressJpg (150 :: Int)) im+      expectedSize = (B.length . image . compressJpg (100 :: Int)) im++  assertBool "Out-of-bounds JpgQuality was not handled properly" (expectedSize == compressedSize)++-- Test that issue #11 is fixed+testJpgInadvertentRotation :: TestTree+testJpgInadvertentRotation = testCase "Compressing a JPEG does not rotate it (issue #11)" $ do+  im <- Image Jpeg <$> B.readFile "tests/data/issue11.jpeg"++  let initial    = either error id $ decodeJpeg (image im)+      compressed = either error id $ decodeJpeg $ image $ compressJpg (50::Int) im++  assertEqual mempty (dynamicMap imageWidth initial) (dynamicMap imageWidth compressed)+  assertEqual mempty (dynamicMap imageHeight initial) (dynamicMap imageHeight compressed)++--------------------------------------------------------------------------------+tests :: TestTree+tests =+  testGroup "Hakyll.Images.CompressJpg.Tests" $+    [testJpgInadvertentRotation] <>+      fromAssertions+          "compressJpg"+          [ testCompressionFromImage,+            testJpgEncodingOutOfLowerBound,+            testJpgEncodingOutOfUpperBound+          ]
tests/Hakyll/Images/Metadata/Tests.hs view
@@ -1,48 +1,48 @@-{-# LANGUAGE OverloadedStrings #-}
-
---------------------------------------------------------------------------------
-module Hakyll.Images.Metadata.Tests
-  ( tests,
-  )
-where
-
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-import qualified Data.ByteString as B
-import Data.Maybe (fromMaybe)
-import Hakyll.Images
-import Hakyll.Images.Common
-import qualified Hakyll.Images.Metadata as M
-import Test.Tasty (TestTree, testGroup)
-import Test.Tasty.HUnit (Assertion, assertBool, testCase)
-import Text.Printf (printf)
-
-fromAssertions ::
-  -- | Name
-  String ->
-  -- | Cases
-  [Assertion] ->
-  -- | Result tests
-  [TestTree]
-fromAssertions name =
-  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]
-
-testJpg :: IO Image
-testJpg = Image Jpeg <$> B.readFile "tests/data/piccolo.jpg"
-
-testMetadata :: Assertion
-testMetadata = do
-  im <- testJpg
-  let meta = metadata im
-      height = fromMaybe 0 (M.lookup M.Height meta)
-      width = fromMaybe 0 (M.lookup M.Width meta)
-
-  -- The following values of (width, height) = (1170, 647) are
-  -- specific to the "piccolo.jpg" image
-  assertBool "Metadata was not decoded properly" (height == 647)
-  assertBool "Metadata was not decoded properly" (width == 1170)
-
-tests :: TestTree
-tests =
-  testGroup "Hakyll.Images.Metadata.Tests" $ fromAssertions "metadata" [testMetadata]
+{-# LANGUAGE OverloadedStrings #-}++--------------------------------------------------------------------------------+module Hakyll.Images.Metadata.Tests+  ( tests,+  )+where++--------------------------------------------------------------------------------++--------------------------------------------------------------------------------+import qualified Data.ByteString as B+import Data.Maybe (fromMaybe)+import Hakyll.Images+import Hakyll.Images.Common+import qualified Hakyll.Images.Metadata as M+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (Assertion, assertBool, testCase)+import Text.Printf (printf)++fromAssertions ::+  -- | Name+  String ->+  -- | Cases+  [Assertion] ->+  -- | Result tests+  [TestTree]+fromAssertions name =+  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]++testJpg :: IO Image+testJpg = Image Jpeg <$> B.readFile "tests/data/piccolo.jpg"++testMetadata :: Assertion+testMetadata = do+  im <- testJpg+  let meta = metadata im+      height = fromMaybe 0 (M.lookup M.Height meta)+      width = fromMaybe 0 (M.lookup M.Width meta)++  -- The following values of (width, height) = (1170, 647) are+  -- specific to the "piccolo.jpg" image+  assertBool "Metadata was not decoded properly" (height == 647)+  assertBool "Metadata was not decoded properly" (width == 1170)++tests :: TestTree+tests =+  testGroup "Hakyll.Images.Metadata.Tests" $ fromAssertions "metadata" [testMetadata]
tests/Hakyll/Images/Resize/Tests.hs view
@@ -1,126 +1,126 @@---------------------------------------------------------------------------------
-module Hakyll.Images.Resize.Tests
-  ( tests,
-  )
-where
-
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-
-import Codec.Picture
-import qualified Data.ByteString as B
-import Data.Ratio ((%))
-import Hakyll.Images
-import Test.HUnit.Approx (assertApproxEqual)
-import Test.Tasty (TestTree, testGroup)
-import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase)
-import Text.Printf (printf)
-
--- Original test image "piccolo.jpg" has shape 1170 x 647px
-testJpg :: IO DynamicImage
-testJpg = do
-  img <- decodeJpeg <$> B.readFile "tests/data/piccolo.jpg"
-  case img of
-    Left _ -> error "Could not decode test picture piccolo.jpg"
-    Right im -> return im
-
-testGif :: IO DynamicImage
-testGif = do
-  img <- decodeGif <$> B.readFile "tests/data/donkey.gif"
-  case img of
-    Left _ -> error "Could not decode test picture donkey.gif"
-    Right im -> return im
-
-fromAssertions ::
-  -- | Name
-  String ->
-  -- | Cases
-  [Assertion] ->
-  -- | Result tests
-  [TestTree]
-fromAssertions name =
-  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]
-
--- Test that the rescaled image is of the appropriate scale
-testResizeJpg :: Assertion
-testResizeJpg = do
-  image <- testJpg
-  let scaledImage = resize 48 64 image
-      converted = convertRGBA8 scaledImage
-      (width, height) =
-        ( imageWidth converted,
-          imageHeight converted
-        )
-  assertEqual "Image width was not resized properly" width 48
-  assertEqual "Image height was not resized properly" height 64
-
--- Test that the rescaled image is of the appropriate scale
-testResizeGif :: Assertion
-testResizeGif = do
-  image <- testGif
-  let scaledImage = resize 48 64 image
-      converted = convertRGBA8 scaledImage
-      (width, height) =
-        ( imageWidth converted,
-          imageHeight converted
-        )
-  assertEqual "Image width was not resized properly" width 48
-  assertEqual "Image height was not resized properly" height 64
-
--- Test that the rescaled image fits in the appropriate box
-testScale :: Assertion
-testScale = do
-  image <- testJpg
-  let scaledImage = scale 600 400 image
-      converted = convertRGBA8 scaledImage
-      (width, height) =
-        ( imageWidth converted,
-          imageHeight converted
-        )
-  assertBool "Image width was not scaled properly" (width <= 600)
-  assertBool "Image height was not scaled properly" (height <= 400)
-  assertBool "Image overall was not scaled properly" (width == 600 || height == 400)
-
--- Test that the images that already fit in dimensions are not scaled
-testEnsureFit :: Assertion
-testEnsureFit = do
-  image <- testJpg
-  let (originalWidth, originalHeight) = (imageWidth . convertRGBA8 $ image, imageHeight . convertRGBA8 $ image)
-      scaledImage = ensureFit 2000 2000 image
-      converted = convertRGBA8 scaledImage
-      (width, height) =
-        ( imageWidth converted,
-          imageHeight converted
-        )
-  assertEqual "Image width was not scaled properly" width originalWidth
-  assertEqual "Image height was not scaled property" height originalHeight
-
--- Test that the rescaled image has the same aspect ratio
-testScalePreservesAspectRatio :: Assertion
-testScalePreservesAspectRatio = do
-  image <- testJpg
-
-  let initialAspectRatio = imageWidth (convertRGBA8 image) % imageHeight (convertRGBA8 image)
-      scaledImage = scale 600 400 image
-      finalAspectRatio = imageWidth (convertRGBA8 scaledImage) % imageHeight (convertRGBA8 scaledImage)
-
-  assertApproxEqual "Aspect ratio was not preserved" 0.02 initialAspectRatio finalAspectRatio
-
---------------------------------------------------------------------------------
-tests :: TestTree
-tests =
-  testGroup "Hakyll.Images.Resize.Tests" $
-    concat
-      [ fromAssertions
-          "rescale"
-          [ testScale,
-            testScalePreservesAspectRatio
-          ],
-        fromAssertions
-          "ensureFit"
-          [testEnsureFit],
-        fromAssertions
-          "resize"
-          [testResizeJpg, testResizeGif]
-      ]
+--------------------------------------------------------------------------------+module Hakyll.Images.Resize.Tests+  ( tests,+  )+where++--------------------------------------------------------------------------------++--------------------------------------------------------------------------------++import Codec.Picture+import qualified Data.ByteString as B+import Data.Ratio ((%))+import Hakyll.Images+import Test.HUnit.Approx (assertApproxEqual)+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase)+import Text.Printf (printf)++-- Original test image "piccolo.jpg" has shape 1170 x 647px+testJpg :: IO DynamicImage+testJpg = do+  img <- decodeJpeg <$> B.readFile "tests/data/piccolo.jpg"+  case img of+    Left _ -> error "Could not decode test picture piccolo.jpg"+    Right im -> return im++testGif :: IO DynamicImage+testGif = do+  img <- decodeGif <$> B.readFile "tests/data/donkey.gif"+  case img of+    Left _ -> error "Could not decode test picture donkey.gif"+    Right im -> return im++fromAssertions ::+  -- | Name+  String ->+  -- | Cases+  [Assertion] ->+  -- | Result tests+  [TestTree]+fromAssertions name =+  zipWith testCase [printf "[%2d] %s" n name | n <- [1 :: Int ..]]++-- Test that the rescaled image is of the appropriate scale+testResizeJpg :: Assertion+testResizeJpg = do+  image <- testJpg+  let scaledImage = resize 48 64 image+      converted = convertRGBA8 scaledImage+      (width, height) =+        ( imageWidth converted,+          imageHeight converted+        )+  assertEqual "Image width was not resized properly" width 48+  assertEqual "Image height was not resized properly" height 64++-- Test that the rescaled image is of the appropriate scale+testResizeGif :: Assertion+testResizeGif = do+  image <- testGif+  let scaledImage = resize 48 64 image+      converted = convertRGBA8 scaledImage+      (width, height) =+        ( imageWidth converted,+          imageHeight converted+        )+  assertEqual "Image width was not resized properly" width 48+  assertEqual "Image height was not resized properly" height 64++-- Test that the rescaled image fits in the appropriate box+testScale :: Assertion+testScale = do+  image <- testJpg+  let scaledImage = scale 600 400 image+      converted = convertRGBA8 scaledImage+      (width, height) =+        ( imageWidth converted,+          imageHeight converted+        )+  assertBool "Image width was not scaled properly" (width <= 600)+  assertBool "Image height was not scaled properly" (height <= 400)+  assertBool "Image overall was not scaled properly" (width == 600 || height == 400)++-- Test that the images that already fit in dimensions are not scaled+testEnsureFit :: Assertion+testEnsureFit = do+  image <- testJpg+  let (originalWidth, originalHeight) = (imageWidth . convertRGBA8 $ image, imageHeight . convertRGBA8 $ image)+      scaledImage = ensureFit 2000 2000 image+      converted = convertRGBA8 scaledImage+      (width, height) =+        ( imageWidth converted,+          imageHeight converted+        )+  assertEqual "Image width was not scaled properly" width originalWidth+  assertEqual "Image height was not scaled property" height originalHeight++-- Test that the rescaled image has the same aspect ratio+testScalePreservesAspectRatio :: Assertion+testScalePreservesAspectRatio = do+  image <- testJpg++  let initialAspectRatio = imageWidth (convertRGBA8 image) % imageHeight (convertRGBA8 image)+      scaledImage = scale 600 400 image+      finalAspectRatio = imageWidth (convertRGBA8 scaledImage) % imageHeight (convertRGBA8 scaledImage)++  assertApproxEqual "Aspect ratio was not preserved" 0.02 initialAspectRatio finalAspectRatio++--------------------------------------------------------------------------------+tests :: TestTree+tests =+  testGroup "Hakyll.Images.Resize.Tests" $+    concat+      [ fromAssertions+          "rescale"+          [ testScale,+            testScalePreservesAspectRatio+          ],+        fromAssertions+          "ensureFit"+          [testEnsureFit],+        fromAssertions+          "resize"+          [testResizeJpg, testResizeGif]+      ]
tests/TestSuite.hs view
@@ -1,27 +1,27 @@---------------------------------------------------------------------------------
-module Main
-  ( main,
-  )
-where
-
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-
-import qualified Hakyll.Images.Common.Tests
-import qualified Hakyll.Images.CompressJpg.Tests
-import qualified Hakyll.Images.Metadata.Tests
-import qualified Hakyll.Images.Resize.Tests
-import Test.Tasty (defaultMain, testGroup)
-
---------------------------------------------------------------------------------
-main :: IO ()
-main =
-  defaultMain $
-    testGroup
-      "Hakyll"
-      [ Hakyll.Images.Common.Tests.tests,
-        Hakyll.Images.CompressJpg.Tests.tests,
-        Hakyll.Images.Resize.Tests.tests,
-        Hakyll.Images.Metadata.Tests.tests
-      ]
+--------------------------------------------------------------------------------+module Main+  ( main,+  )+where++--------------------------------------------------------------------------------++--------------------------------------------------------------------------------++import qualified Hakyll.Images.Common.Tests+import qualified Hakyll.Images.CompressJpg.Tests+import qualified Hakyll.Images.Metadata.Tests+import qualified Hakyll.Images.Resize.Tests+import Test.Tasty (defaultMain, testGroup)++--------------------------------------------------------------------------------+main :: IO ()+main =+  defaultMain $+    testGroup+      "Hakyll"+      [ Hakyll.Images.Common.Tests.tests,+        Hakyll.Images.CompressJpg.Tests.tests,+        Hakyll.Images.Resize.Tests.tests,+        Hakyll.Images.Metadata.Tests.tests+      ]
+ tests/data/issue11.jpeg view

file too large to diff