Skip to content

Instantly share code, notes, and snippets.

@nyg
Last active February 21, 2021 05:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nyg/bdeae8190a41b4b56bde8e13dd471ecc to your computer and use it in GitHub Desktop.
Save nyg/bdeae8190a41b4b56bde8e13dd471ecc to your computer and use it in GitHub Desktop.
Add a JPEG comment marker to file in pure Swift.
//
// Inspired by wrjpgcom of libjpeg
// https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/wrjpgcom.c
//
// https://stackoverflow.com/a/46045524/5536516
//
// Note: To add an EXIF UserComment go here:
// https://gist.github.com/nyg/c90f36abbd30f72c8b6681ef23db886b
import Foundation
/**
Adds a comment to the given JPEG image.
The comment marker (COM) will be insert just before the first SOF
marker found.
1. It causes the new comment to appear after, rather than before,
existing comments,
2. and ensures that comments come after any JFIF or JFXX markers,
as required by the JFIF specification.
- parameters:
- jpegData: The JPEG image as a Data instance. Will be modified
by the function.
- comment: The comment that will be added to the image.
*/
func addJPEGComment(to jpegData: inout Data, _ comment: String) {
// Define JPEG markers.
let markerStart: UInt8 = 0xFF
let comMarker: UInt8 = 0xFE
let sofMarkers: [UInt8] = [
0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF,
0xD9 // End of Image
]
// Find range of first SOF marker, or EOI.
var firstSOFRange: Range<Data.Index>?
for marker in sofMarkers {
if let range = jpegData.range(of: Data(bytes: [ markerStart, marker ])) {
firstSOFRange = range
break
}
}
guard let firstSOFIndex = firstSOFRange?.lowerBound
else { fatalError("No SOF or EOI marker found.") }
// Create the comment byte array.
let length = comment.lengthOfBytes(using: .utf8) + 2
let l1 = UInt8(length >> 8) & markerStart
let l2 = UInt8(length) & markerStart
let commentArray = [ markerStart, comMarker, l1, l2 ] + [UInt8](comment.utf8)
// Insert the comment array into image data object.
jpegData.insert(contentsOf: commentArray, at: firstSOFIndex)
}
/* Usage example */
guard let jpegURL = Bundle.main.url(forResource: "image", withExtension: "jpg")
else { fatalError("File not found.") }
guard var jpegData = try? Data(contentsOf: jpegURL)
else { fatalError("File could not be read.") }
addJPEGComment(to: &jpegData, "This is a JPEG comment.")
guard let jpegOutputURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("image_com.jpg")
else { fatalError("Destination URL not created") }
try jpegData.write(to: jpegOutputURL)
print("rdjpgcom \(jpegOutputURL.path)")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment