diff --git a/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionFetcher.swift b/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionFetcher.swift deleted file mode 100644 index 28d4a693619d..000000000000 --- a/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionFetcher.swift +++ /dev/null @@ -1,87 +0,0 @@ -import UIKit - -class ImageDimensionsFetcher: NSObject, URLSessionDataDelegate { - // Helpful typealiases for the closures - public typealias CompletionHandler = (ImageDimensionFormat, CGSize?) -> Void - public typealias ErrorHandler = (Error?) -> Void - - let completionHandler: CompletionHandler - let errorHandler: ErrorHandler? - - // Internal use properties - private let request: URLRequest - private var task: URLSessionDataTask? = nil - private let parser: ImageDimensionParser - private var session: URLSession? = nil - - deinit { - cancel() - } - - init(request: URLRequest, - success: @escaping CompletionHandler, - error: ErrorHandler? = nil, - imageParser: ImageDimensionParser = ImageDimensionParser()) { - self.request = request - self.completionHandler = success - self.errorHandler = error - self.parser = imageParser - - super.init() - } - - /// Starts the calculation process - func start() { - let config = URLSessionConfiguration.default - let session = URLSession(configuration: config, delegate: self, delegateQueue: nil) - let task = session.dataTask(with: request) - task.resume() - - self.task = task - self.session = session - } - - func cancel() { - session?.invalidateAndCancel() - task?.cancel() - } - - // MARK: - URLSessionDelegate - public func urlSession(_ session: URLSession, task dataTask: URLSessionTask, didCompleteWithError error: Error?) { - // Don't trigger an error if we cancelled the task - if let error, (error as NSError).code == NSURLErrorCancelled { - return - } - - self.errorHandler?(error) - } - - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - // Add the downloaded data to the parser - parser.append(bytes: data) - - // Wait for the format to be detected - guard let format = parser.format else { - return - } - - // Check if the format is unsupported - guard format != .unsupported else { - completionHandler(format, nil) - - // We can't parse unsupported images, cancel the download - cancel() - return - } - - // Wait for the image size - guard let size = parser.imageSize else { - return - } - - completionHandler(format, size) - - // The image size has been calculated, stop downloading - cancel() - } -} diff --git a/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionParser.swift b/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionParser.swift deleted file mode 100644 index de2726fe5a58..000000000000 --- a/WordPress/Classes/Utility/Image Dimension Parser/ImageDimensionParser.swift +++ /dev/null @@ -1,268 +0,0 @@ -import UIKit - -class ImageDimensionParser { - private(set) var format: ImageDimensionFormat? - private(set) var imageSize: CGSize? = nil - - private var data: Data - - init(with data: Data = Data()) { - self.data = data - - parse() - } - - public func append(bytes: Data) { - data.append(contentsOf: bytes) - - parse() - } - - private func parse() { - guard - let format = ImageDimensionFormat(with: data) - else { - return - } - - self.format = format - imageSize = dimensions(with: data) - - guard imageSize != nil else { - return - } - } - - // MARK: - Dimension Calculating - private func dimensions(with data: Data) -> CGSize? { - switch format { - case .png: return pngSize(with: data) - case .gif: return gifSize(with: data) - case .jpeg: return jpegSize(with: data) - - default: return nil - } - } - - // MARK: - PNG Parsing - private func pngSize(with data: Data) -> CGSize? { - // Bail out if the data size is too small to read the header - let chunkSize = PNGConstants.chunkSize - let ihdrStart = PNGConstants.headerSize + chunkSize - - // The min length needed to read the width / height - let minLength = ihdrStart + chunkSize * 3 - - guard data.count >= minLength else { - return nil - } - - // Validate the header to make sure the width/height is in the correct spot - guard data.subdata(start: ihdrStart, length: chunkSize) == PNGConstants.IHDR else { - return nil - } - - // Width is immediately after the IHDR header - let widthOffset = ihdrStart + chunkSize - - // Height is after the width - let heightOffset = widthOffset + chunkSize - - // Height and width are stored as 32 bit ints - // http://www.libpng.org/pub/png/spec/1.0/PNG-Chunks.html - // ^ The maximum for each is (2^31)-1 in order to accommodate languages that have difficulty with unsigned 4-byte values. - let width = CFSwapInt32(data[widthOffset, chunkSize] as UInt32) - let height = CFSwapInt32(data[heightOffset, chunkSize] as UInt32) - - return CGSize(width: Int(width), height: Int(height)) - } - - private struct PNGConstants { - // PNG header size is 8 bytes - static let headerSize = 8 - - // PNG is broken up into 4 byte chunks, except for the header - static let chunkSize = 4 - - // IHDR header: // https://www.w3.org/TR/PNG/#11IHDR - static let IHDR = Data([0x49, 0x48, 0x44, 0x52]) - } - - // MARK: - GIF Parsing - private func gifSize(with data: Data) -> CGSize? { - // Bail out if the data size is too small to read the header - let valueSize = GIFConstants.valueSize - let headerSize = GIFConstants.headerSize - - // Min length we need to read is the header size + 4 bytes - let minLength = headerSize + valueSize * 3 - - guard data.count >= minLength else { - return nil - } - - // The width appears directly after the header, and the height after that. - let widthOffset = headerSize - let heightOffset = widthOffset - - // Reads the "logical screen descriptor" which appears after the GIF header block - let width: UInt16 = data[widthOffset, valueSize] - let height: UInt16 = data[heightOffset, valueSize] - - return CGSize(width: Int(width), height: Int(height)) - } - - private struct GIFConstants { - // http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp - - // The GIF header size is 6 bytes - static let headerSize = 6 - - // The height and width are stored as 2 byte values - static let valueSize = 2 - } - - // MARK: - JPEG Parsing - private struct JPEGConstants { - static let blockSize: UInt16 = 256 - - // 16 bytes skips the header and the first block - static let minDataCount = 16 - - static let valueSize = 2 - static let heightOffset = 5 - - // JFIF{NULL} - static let jfifHeader = Data([0x4A, 0x46, 0x49, 0x46, 0x00]) - } - - private func jpegSize(with data: Data) -> CGSize? { - // Bail out if the data size is too small to read the header - guard data.count > JPEGConstants.minDataCount else { - return nil - } - - // Adapted from: - // - https://web.archive.org/web/20131016210645/http://www.64lines.com/jpeg-width-height - - var i = JPEGConstants.jfifHeader.count - 1 - - let blockSize: UInt16 = JPEGConstants.blockSize - - // Retrieve the block length of the first block since the first block will not contain the size of file - var block_length = UInt16(data[i]) * blockSize + UInt16(data[i+1]) - - while i < data.count { - i += Int(block_length) - - // Protect again out of bounds issues - // 10 = the max size we need to read all the values from below - if i + 10 >= data.count { - return nil - } - - // Check that we are truly at the start of another block - if data[i] != 0xFF { - return nil - } - - // SOFn marker - let marker = data[i+1] - - let isValidMarker = (marker >= 0xC0 && marker <= 0xC3) || - (marker >= 0xC5 && marker <= 0xC7) || - (marker >= 0xC9 && marker <= 0xCB) || - (marker >= 0xCD && marker <= 0xCF) - - if isValidMarker { - // "Start of frame" marker which contains the file size - let valueSize = JPEGConstants.valueSize - let heightOffset = i + JPEGConstants.heightOffset - let widthOffset = heightOffset + valueSize - - let height = CFSwapInt16(data[heightOffset, valueSize] as UInt16) - let width = CFSwapInt16(data[widthOffset, valueSize] as UInt16) - - return CGSize(width: Int(width), height: Int(height)) - } - - // Go to the next block - i += 2 // Skip the block marker - block_length = UInt16(data[i]) * blockSize + UInt16(data[i+1]) - } - - return nil - } -} - -// MARK: - ImageFormat -enum ImageDimensionFormat { - // WordPress supported image formats: - // https://wordpress.com/support/images/ - // https://codex.wordpress.org/Uploading_Files - case jpeg - case png - case gif - case unsupported - - init?(with data: Data) { - if data.headerIsEqual(to: FileMarker.jpeg) { - self = .jpeg - } - else if data.headerIsEqual(to: FileMarker.gif) { - self = .gif - } - else if data.headerIsEqual(to: FileMarker.png) { - self = .png - } - else if data.count < FileMarker.png.count { - return nil - } - else { - self = .unsupported - } - } - - // File type markers denote the type of image in the first few bytes of the file - private struct FileMarker { - // https://en.wikipedia.org/wiki/JPEG_Network_Graphics - static let png = Data([0x89, 0x50, 0x4E, 0x47]) - - // https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format - // FFD8 = SOI, APP0 marker - static let jpeg = Data([0xFF, 0xD8, 0xFF]) - - // https://en.wikipedia.org/wiki/GIF - static let gif = Data([0x47, 0x49, 0x46, 0x38]) //GIF8 - } -} - -// MARK: - Private: Extensions -private extension Data { - func headerData(with length: Int) -> Data { - return subdata(start: 0, length: length) - } - - func headerIsEqual(to value: Data) -> Bool { - // Prevent any out of bounds issues - if count < value.count { - return false - } - - let header = headerData(with: value.count) - - return header == value - } - - func subdata(start: Int, length: Int) -> Data { - return subdata(in: start ..< start + length) - } - - subscript(range: Range) -> UInt16 { - return subdata(in: range).withUnsafeBytes { $0.load(as: UInt16.self) } - } - - subscript(start: Int, length: Int) -> T { - return self[start.. Void - typealias ImageLoaderFailureBlock = (Error?) -> Void - - // MARK: Public Fields - - public var photonQuality: UInt { - get { - return selectedPhotonQuality - } - set(newPhotonQuality) { - selectedPhotonQuality = min(max(newPhotonQuality, Constants.minPhotonQuality), Constants.maxPhotonQuality) - } - } - - // MARK: - Image Dimensions Support - typealias ImageLoaderDimensionsBlock = (ImageDimensionFormat, CGSize) -> Void - - /// Called if the imageLoader is able to determine the image format, and dimensions - /// for the image prior to it being downloaded. - /// Note: Set the property prior to calling any load method - public var imageDimensionsHandler: ImageLoaderDimensionsBlock? - private var imageDimensionsFetcher: ImageDimensionsFetcher? = nil - - // MARK: Private Fields - - private unowned let imageView: CachedAnimatedImageView - private let loadingIndicator: ActivityIndicatorType - - private var successHandler: ImageLoaderSuccessBlock? - private var errorHandler: ImageLoaderFailureBlock? - private var placeholder: UIImage? - private var selectedPhotonQuality: UInt = Constants.defaultPhotonQuality - - @objc init(imageView: CachedAnimatedImageView, gifStrategy: GIFStrategy = .mediumGIFs) { - self.imageView = imageView - imageView.gifStrategy = gifStrategy - - let loadingIndicator = CircularProgressView(style: .primary) - loadingIndicator.backgroundColor = .clear - self.loadingIndicator = loadingIndicator - - super.init() - - imageView.addLoadingIndicator(self.loadingIndicator, style: .fullView) - } - - /// Removes the gif animation and prevents it from animate again. - /// Call this in a table/collection cell's `prepareForReuse()`. - /// - @objc func prepareForReuse() { - imageView.prepForReuse() - } - - /// Load an image from a specific post, using the given URL. Supports animated images (gifs) as well. - /// - /// - Parameters: - /// - url: The URL to load the image from. - /// - host: The `MediaHost` of the image. - /// - size: The preferred size of the image to load. - /// - func loadImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { - if url.isFileURL { - downloadImage(from: url) - } else if url.isGif { - loadGif(with: url, from: host, preferredSize: size) - } else { - imageView.clean() - loadStaticImage(with: url, from: host, preferredSize: size) - } - } - - @objc(loadImageWithURL:fromPost:preferredSize:placeholder:success:error:) - func loadImage(with url: URL, from post: AbstractPost, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { - - let host = MediaHost(post) - loadImage(with: url, from: host, preferredSize: size, placeholder: placeholder, success: success, error: error) - } - - @objc(loadImageWithURL:fromReaderPost:preferredSize:placeholder:success:error:) - func loadImage(with url: URL, from readerPost: ReaderPost, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { - loadImage(with: url, from: MediaHost(readerPost), preferredSize: size, placeholder: placeholder, success: success, error: error) - } - - /// Load an image from a specific post, using the given URL. Supports animated images (gifs) as well. - /// - /// - Parameters: - /// - url: The URL to load the image from. - /// - host: The host of the image. - /// - size: The preferred size of the image to load. You can pass height 0 to set width and preserve aspect ratio. - /// - placeholder: A placeholder to show while the image is loading. - /// - success: A closure to be called if the image was loaded successfully. - /// - error: A closure to be called if there was an error loading the image. - func loadImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { - - self.placeholder = placeholder - successHandler = success - errorHandler = error - - loadImage(with: url, from: host, preferredSize: size) - } - - // MARK: - Private helpers - - /// Load an animated image from the given URL. - /// - private func loadGif(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { - let mediaAuthenticator = MediaRequestAuthenticator() - mediaAuthenticator.authenticatedRequest( - for: url, - from: host, - onComplete: { request in - self.downloadGif(from: request) - }, - onFailure: { error in - WordPressAppDelegate.crashLogging?.logError(error) - self.callErrorHandler(with: error) - }) - } - - /// Load a static image from the given URL. - /// - private func loadStaticImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { - let finalURL: URL - - switch host { - case .publicSite: fallthrough - case .privateSelfHostedSite: - finalURL = url - case .publicWPComSite: fallthrough - case .privateAtomicWPComSite: - finalURL = photonUrl(with: url, preferredSize: size) - case .privateWPComSite: - finalURL = privateImageURL(with: url, from: host, preferredSize: size) - } - - let mediaRequestAuthenticator = MediaRequestAuthenticator() - - mediaRequestAuthenticator.authenticatedRequest(for: finalURL, from: host, onComplete: { request in - self.downloadImage(from: request) - }) { error in - WordPressAppDelegate.crashLogging?.logError(error) - self.callErrorHandler(with: error) - } - } - - /// Constructs the URL for an image from a private post hosted in WPCom. - /// - private func privateImageURL(with url: URL, from host: MediaHost, preferredSize size: CGSize) -> URL { - let scale = UIScreen.main.scale - let scaledSize = CGSize(width: size.width * scale, height: size.height * scale) - let scaledURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: url) - - return scaledURL - } - - /// Gets the photon URL with the specified size, or returns the passed `URL` - /// - private func photonUrl(with url: URL, preferredSize size: CGSize) -> URL { - guard let photonURL = getPhotonUrl(for: url, size: size) else { - return url - } - - return photonURL - } - - /// Triggers the image dimensions fetcher if the `imageDimensionsHandler` property is set - private func calculateImageDimensionsIfNeeded(from request: URLRequest) { - guard let imageDimensionsHandler else { - return - } - - let fetcher = ImageDimensionsFetcher(request: request, success: { (format, size) in - guard let size, size != .zero else { - return - } - - DispatchQueue.main.async { - imageDimensionsHandler(format, size) - } - }) - - fetcher.start() - - imageDimensionsFetcher = fetcher - } - - /// Stop the image dimension calculation - private func cancelImageDimensionCalculation() { - imageDimensionsFetcher?.cancel() - imageDimensionsFetcher = nil - } - - /// Download the animated image from the given URL Request. - /// - private func downloadGif(from request: URLRequest) { - calculateImageDimensionsIfNeeded(from: request) - - imageView.startLoadingAnimation() - imageView.setAnimatedImage(request, placeholderImage: placeholder, success: { [weak self] in - self?.callSuccessHandler() - }) { [weak self] (error) in - self?.callErrorHandler(with: error) - } - } - - /// Downloads the image from the given URL Request. - /// - private func downloadImage(from request: URLRequest) { - calculateImageDimensionsIfNeeded(from: request) - - imageView.startLoadingAnimation() - imageView.af.setImage(withURLRequest: request, completion: { [weak self] dataResponse in - guard let self else { - return - } - - switch dataResponse.result { - case .success: - self.callSuccessHandler() - case .failure(let error): - self.callErrorHandler(with: error) - } - }) - } - - /// Downloads the image from the given URL. - /// - private func downloadImage(from url: URL) { - let request = URLRequest(url: url) - downloadImage(from: request) - } - - private func callSuccessHandler() { - cancelImageDimensionCalculation() - - imageView.stopLoadingAnimation() - guard successHandler != nil else { - return - } - DispatchQueue.main.async { - self.successHandler?() - } - } - - private func callErrorHandler(with error: Error?) { - if let error, (error as NSError).code == NSURLErrorCancelled { - return - } - - cancelImageDimensionCalculation() - - DispatchQueue.main.async { [weak self] in - guard let self else { - return - } - - if self.imageView.shouldShowLoadingIndicator { - (self.loadingIndicator as? CircularProgressView)?.state = .error - } - - self.errorHandler?(error) - } - } -} - -// MARK: - Loading Media object - -extension ImageLoader { - private func getPhotonUrl(for url: URL, size: CGSize) -> URL? { - var finalSize = size - if url.isGif { - // Photon helper sets the size to load the retina version. We don't want that for gifs - let scale = UIScreen.main.scale - finalSize = CGSize(width: size.width / scale, height: size.height / scale) - } - return PhotonImageURLHelper.photonURL(with: finalSize, - forImageURL: url, - forceResize: true, - imageQuality: selectedPhotonQuality) - } -} - -// MARK: - Constants - -private extension ImageLoader { - enum Constants { - static let minPhotonQuality: UInt = 1 - static let maxPhotonQuality: UInt = 100 - static let defaultPhotonQuality: UInt = 80 - } -} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 3bba6654b1e5..2d1bfab4901d 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -342,15 +342,6 @@ 2FAE97090E33B21600CA8540 /* defaultPostTemplate_old.html in Resources */ = {isa = PBXBuildFile; fileRef = 2FAE97040E33B21600CA8540 /* defaultPostTemplate_old.html */; }; 2FAE970C0E33B21600CA8540 /* xhtml1-transitional.dtd in Resources */ = {isa = PBXBuildFile; fileRef = 2FAE97070E33B21600CA8540 /* xhtml1-transitional.dtd */; }; 2FAE970D0E33B21600CA8540 /* xhtmlValidatorTemplate.xhtml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAE97080E33B21600CA8540 /* xhtmlValidatorTemplate.xhtml */; }; - 32110547250BFC3E0048446F /* ImageDimensionParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32110546250BFC3E0048446F /* ImageDimensionParserTests.swift */; }; - 3211055A250C027D0048446F /* invalid-jpeg-header.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 32110552250C027B0048446F /* invalid-jpeg-header.jpg */; }; - 3211055B250C027D0048446F /* valid-jpeg-header.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 32110553250C027B0048446F /* valid-jpeg-header.jpg */; }; - 3211055C250C027D0048446F /* valid-gif-header.gif in Resources */ = {isa = PBXBuildFile; fileRef = 32110554250C027B0048446F /* valid-gif-header.gif */; }; - 3211055D250C027D0048446F /* 100x100.gif in Resources */ = {isa = PBXBuildFile; fileRef = 32110555250C027C0048446F /* 100x100.gif */; }; - 32110560250C027D0048446F /* invalid-gif.gif in Resources */ = {isa = PBXBuildFile; fileRef = 32110558250C027C0048446F /* invalid-gif.gif */; }; - 32110561250C027D0048446F /* 100x100.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 32110559250C027C0048446F /* 100x100.jpg */; }; - 32110569250C0E960048446F /* 100x100-png in Resources */ = {isa = PBXBuildFile; fileRef = 32110568250C0E960048446F /* 100x100-png */; }; - 3211056B250C0F750048446F /* valid-png-header in Resources */ = {isa = PBXBuildFile; fileRef = 3211056A250C0F750048446F /* valid-png-header */; }; 321955C124BE4EBF00E3F316 /* ReaderSelectInterestsCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321955C024BE4EBF00E3F316 /* ReaderSelectInterestsCoordinatorTests.swift */; }; 3236F7A124B61B950088E8F3 /* ReaderInterestsDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3236F7A024B61B950088E8F3 /* ReaderInterestsDataSourceTests.swift */; }; 323F8F3023A22C4C000BA49C /* SiteCreationRotatingMessageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C6CDDA23A1FF0D002556FF /* SiteCreationRotatingMessageViewTests.swift */; }; @@ -2213,15 +2204,6 @@ 2FAE97040E33B21600CA8540 /* defaultPostTemplate_old.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = defaultPostTemplate_old.html; path = Resources/HTML/defaultPostTemplate_old.html; sourceTree = ""; }; 2FAE97070E33B21600CA8540 /* xhtml1-transitional.dtd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = "xhtml1-transitional.dtd"; path = "Resources/HTML/xhtml1-transitional.dtd"; sourceTree = ""; }; 2FAE97080E33B21600CA8540 /* xhtmlValidatorTemplate.xhtml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = xhtmlValidatorTemplate.xhtml; path = Resources/HTML/xhtmlValidatorTemplate.xhtml; sourceTree = ""; }; - 32110546250BFC3E0048446F /* ImageDimensionParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDimensionParserTests.swift; sourceTree = ""; }; - 32110552250C027B0048446F /* invalid-jpeg-header.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "invalid-jpeg-header.jpg"; sourceTree = ""; }; - 32110553250C027B0048446F /* valid-jpeg-header.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "valid-jpeg-header.jpg"; sourceTree = ""; }; - 32110554250C027B0048446F /* valid-gif-header.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = "valid-gif-header.gif"; sourceTree = ""; }; - 32110555250C027C0048446F /* 100x100.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 100x100.gif; sourceTree = ""; }; - 32110558250C027C0048446F /* invalid-gif.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = "invalid-gif.gif"; sourceTree = ""; }; - 32110559250C027C0048446F /* 100x100.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 100x100.jpg; sourceTree = ""; }; - 32110568250C0E960048446F /* 100x100-png */ = {isa = PBXFileReference; lastKnownFileType = file; path = "100x100-png"; sourceTree = ""; }; - 3211056A250C0F750048446F /* valid-png-header */ = {isa = PBXFileReference; lastKnownFileType = file; path = "valid-png-header"; sourceTree = ""; }; 321955C024BE4EBF00E3F316 /* ReaderSelectInterestsCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSelectInterestsCoordinatorTests.swift; sourceTree = ""; }; 3236F7A024B61B950088E8F3 /* ReaderInterestsDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderInterestsDataSourceTests.swift; sourceTree = ""; }; 32387A1D541851E82ED957CE /* Pods-WordPressShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressShareExtension.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressShareExtension/Pods-WordPressShareExtension.release.xcconfig"; sourceTree = ""; }; @@ -4111,6 +4093,7 @@ 08F8CD301EBD2A960049D0C0 /* MediaImageExporterTests.swift */, 08F8CD3A1EBD2D020049D0C0 /* MediaURLExporterTests.swift */, 08E77F461EE9D72F006F9515 /* MediaThumbnailExporterTests.swift */, + 0C2518AD2ABE1EA000381D31 /* iphone-photo.heic */, 0C8FC9A92A8C57000059DCE4 /* ItemProviderMediaExporterTests.swift */, ); name = Media; @@ -4567,31 +4550,6 @@ name = Frameworks; sourceTree = ""; }; - 32110548250BFC5A0048446F /* Image Dimension Parser */ = { - isa = PBXGroup; - children = ( - 32110549250BFD4E0048446F /* Test Images */, - 32110546250BFC3E0048446F /* ImageDimensionParserTests.swift */, - ); - name = "Image Dimension Parser"; - sourceTree = ""; - }; - 32110549250BFD4E0048446F /* Test Images */ = { - isa = PBXGroup; - children = ( - 32110568250C0E960048446F /* 100x100-png */, - 32110555250C027C0048446F /* 100x100.gif */, - 32110559250C027C0048446F /* 100x100.jpg */, - 32110558250C027C0048446F /* invalid-gif.gif */, - 0C2518AD2ABE1EA000381D31 /* iphone-photo.heic */, - 32110552250C027B0048446F /* invalid-jpeg-header.jpg */, - 32110554250C027B0048446F /* valid-gif-header.gif */, - 32110553250C027B0048446F /* valid-jpeg-header.jpg */, - 3211056A250C0F750048446F /* valid-png-header */, - ); - path = "Test Images"; - sourceTree = ""; - }; 3236F79F24B61B780088E8F3 /* Select Interests */ = { isa = PBXGroup; children = ( @@ -6895,7 +6853,6 @@ 175CC17327205BDC00622FB4 /* Domains */, E1C9AA541C1041E600732665 /* Extensions */, FF9A6E6F21F9359200D36D14 /* Gutenberg */, - 32110548250BFC5A0048446F /* Image Dimension Parser */, 803DE81D29063689007D4E9C /* Jetpack */, FF7C89A11E3A1029000472A8 /* MediaPicker */, 5D7A577D1AFBFD7C0097C028 /* Models */, @@ -8695,9 +8652,7 @@ 7E4A772520F7C5E5001C706D /* activity-log-theme-content.json in Resources */, D848CBF720FEEE7F00A9038F /* notifications-text-content.json in Resources */, D848CC0F20FF2D9B00A9038F /* notifications-comment-range.json in Resources */, - 3211055A250C027D0048446F /* invalid-jpeg-header.jpg in Resources */, E15027621E03E51500B847E3 /* notes-action-push.json in Resources */, - 3211056B250C0F750048446F /* valid-png-header in Resources */, 0C6C4CD42A4F0AD90049E762 /* blaze-search-page-1.json in Resources */, 748BD8851F19234300813F9A /* notifications-mark-as-read.json in Resources */, 7E442FCA20F678D100DEACA5 /* activity-log-pingback-content.json in Resources */, @@ -8729,7 +8684,6 @@ D88A64AA208D974D008AE9BC /* thumbnail-collection.json in Resources */, 08B832421EC130D60079808D /* test-gif.gif in Resources */, D821C819210037F8002ED995 /* activity-log-activity-content.json in Resources */, - 3211055D250C027D0048446F /* 100x100.gif in Resources */, 748437EB1F1D4A4800E8DDAF /* gallery-reader-post-private.json in Resources */, 465F8A0A263B692600F4C950 /* wp-block-editor-v1-settings-success-ThemeJSON.json in Resources */, D848CBFB20FEFA4800A9038F /* notifications-comment-content.json in Resources */, @@ -8739,16 +8693,13 @@ E11330511A13BAA300D36D84 /* me-sites-with-jetpack.json in Resources */, 4A76A4BF29D4F0A500AABF4B /* reader-post-comments-success.json in Resources */, 7E53AB0820FE6C9C005796FE /* activity-log-post.json in Resources */, - 32110560250C027D0048446F /* invalid-gif.gif in Resources */, D848CC0B20FF2D5D00A9038F /* notifications-user-range.json in Resources */, 855408861A6F105700DDBD79 /* app-review-prompt-all-enabled.json in Resources */, - 32110569250C0E960048446F /* 100x100-png in Resources */, 0C8FC9AC2A8C57930059DCE4 /* test-webp.webp in Resources */, DC772B0428200A3700664C02 /* stats-visits-day-11.json in Resources */, D848CC1320FF31BB00A9038F /* notifications-blockquote-range.json in Resources */, D848CC0D20FF2D7C00A9038F /* notifications-post-range.json in Resources */, 8B45C12627B2A27400EA3257 /* dashboard-200-with-drafts-only.json in Resources */, - 32110561250C027D0048446F /* 100x100.jpg in Resources */, 748437F11F1D4ECC00E8DDAF /* notifications-last-seen.json in Resources */, 74585B9D1F0D591D00E7E667 /* domain-service-all-domain-types.json in Resources */, E131CB5616CACF1E004B0314 /* get-user-blogs_has-blog.json in Resources */, @@ -8770,14 +8721,12 @@ FE003F62282E73E6006F8D1D /* blogging-prompts-fetch-success.json in Resources */, 46CFA7BF262745F70077BAD9 /* get_wp_v2_themes_twentytwentyone.json in Resources */, 465F89F7263B690C00F4C950 /* wp-block-editor-v1-settings-success-NotThemeJSON.json in Resources */, - 3211055C250C027D0048446F /* valid-gif-header.gif in Resources */, D848CC0920FF2D4400A9038F /* notifications-icon-range.json in Resources */, E131CB5816CACFB4004B0314 /* get-user-blogs_doesnt-have-blog.json in Resources */, 08DF9C441E8475530058678C /* test-image-portrait.jpg in Resources */, B5AEEC7C1ACACFDA008BF2A4 /* notifications-new-follower.json in Resources */, FA6C32C72BF2588C00BBDDB4 /* app-store-lookup-response.json in Resources */, C8567494243F388F001A995E /* tenor-invalid-search-reponse.json in Resources */, - 3211055B250C027D0048446F /* valid-jpeg-header.jpg in Resources */, B5AEEC791ACACFDA008BF2A4 /* notifications-badge.json in Resources */, 46CFA7E3262746940077BAD9 /* get_wp_v2_themes_twentytwenty.json in Resources */, D848CC1120FF310400A9038F /* notifications-site-range.json in Resources */, @@ -10149,7 +10098,6 @@ 57B71D4E230DB5F200789A68 /* BlogBuilder.swift in Sources */, F406F3ED2B55960700AFC04A /* CompliancePopoverCoordinatorTests.swift in Sources */, D848CC1920FF3A2400A9038F /* FormattableNotIconTests.swift in Sources */, - 32110547250BFC3E0048446F /* ImageDimensionParserTests.swift in Sources */, E1AB5A3A1E0C464700574B4E /* DelayTests.swift in Sources */, D848CC0320FF04FA00A9038F /* FormattableUserContentTests.swift in Sources */, F41D98E82B39E14F004EC050 /* DashboardDynamicCardAnalyticsEventTests.swift in Sources */, diff --git a/WordPress/WordPressTest/ImageDimensionParserTests.swift b/WordPress/WordPressTest/ImageDimensionParserTests.swift deleted file mode 100644 index 4c08d9ae42a7..000000000000 --- a/WordPress/WordPressTest/ImageDimensionParserTests.swift +++ /dev/null @@ -1,65 +0,0 @@ -import XCTest -@testable import WordPress - -class ImageDimensionParserTests: XCTestCase { - - // MARK: - Valid Header Tests - /// Test a file that has a valid JPEG header - func testValidJPEGHeader() { - let data = dataForFile(with: "valid-jpeg-header.jpg") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.jpeg) - } - - /// Test a file that has a valid JPEG header, but no other data - func testValidPNGHeader() { - let data = dataForFile(with: "valid-png-header") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.png) - } - - /// Test a file that has a valid GIF header - func testValidGIFHeader() { - let data = dataForFile(with: "valid-gif-header.gif") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.gif) - } - - // MARK: - Validate Sizes - - /// Test a 100x100 JPEG file - func testJPEGDimensions() { - let data = dataForFile(with: "100x100.jpg") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.jpeg) - XCTAssertEqual(parser.imageSize, CGSize(width: 100, height: 100)) - } - - /// Test a 100x100 PNG file - func testPNGDimensions() { - let data = dataForFile(with: "100x100-png") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.png) - XCTAssertEqual(parser.imageSize, CGSize(width: 100, height: 100)) - } - - /// Test a 100x100 GIF file - func testGIFDimensions() { - let data = dataForFile(with: "100x100.gif") - let parser = ImageDimensionParser(with: data) - - XCTAssertEqual(parser.format, ImageDimensionFormat.gif) - XCTAssertEqual(parser.imageSize, CGSize(width: 100, height: 100)) - } - - // MARK: - Private: Helpers - private func dataForFile(with name: String) -> Data { - let url = Bundle(for: ImageDimensionParserTests.self).url(forResource: name, withExtension: nil)! - return try! Data(contentsOf: url) - } -} diff --git a/WordPress/WordPressTest/Test Images/100x100-png b/WordPress/WordPressTest/Test Images/100x100-png deleted file mode 100644 index 3fc17a04dfc5..000000000000 Binary files a/WordPress/WordPressTest/Test Images/100x100-png and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/100x100.gif b/WordPress/WordPressTest/Test Images/100x100.gif deleted file mode 100644 index f70ac685ee57..000000000000 Binary files a/WordPress/WordPressTest/Test Images/100x100.gif and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/100x100.jpg b/WordPress/WordPressTest/Test Images/100x100.jpg deleted file mode 100644 index db3980bd4aa9..000000000000 Binary files a/WordPress/WordPressTest/Test Images/100x100.jpg and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/invalid-gif.gif b/WordPress/WordPressTest/Test Images/invalid-gif.gif deleted file mode 100755 index 5fb2c25c410d..000000000000 Binary files a/WordPress/WordPressTest/Test Images/invalid-gif.gif and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/invalid-jpeg-header.jpg b/WordPress/WordPressTest/Test Images/invalid-jpeg-header.jpg deleted file mode 100755 index 19b8194f7d8c..000000000000 Binary files a/WordPress/WordPressTest/Test Images/invalid-jpeg-header.jpg and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/valid-gif-header.gif b/WordPress/WordPressTest/Test Images/valid-gif-header.gif deleted file mode 100755 index c9f3ee173650..000000000000 --- a/WordPress/WordPressTest/Test Images/valid-gif-header.gif +++ /dev/null @@ -1 +0,0 @@ -GIF89a \ No newline at end of file diff --git a/WordPress/WordPressTest/Test Images/valid-jpeg-header.jpg b/WordPress/WordPressTest/Test Images/valid-jpeg-header.jpg deleted file mode 100755 index ae4a6ef503de..000000000000 Binary files a/WordPress/WordPressTest/Test Images/valid-jpeg-header.jpg and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/valid-png-header b/WordPress/WordPressTest/Test Images/valid-png-header deleted file mode 100755 index 8c6519eefd84..000000000000 Binary files a/WordPress/WordPressTest/Test Images/valid-png-header and /dev/null differ diff --git a/WordPress/WordPressTest/Test Images/iphone-photo.heic b/WordPress/WordPressTest/iphone-photo.heic similarity index 100% rename from WordPress/WordPressTest/Test Images/iphone-photo.heic rename to WordPress/WordPressTest/iphone-photo.heic