Tuesday, May 11, 2021

#1 2017-11-09 05:33:07 am

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Generate QR code (offline)

I know that it is possible to create a QR code using AppleScript and a curl command to download, then open a QR code generated with chart.apis.google.com for example.

This required an active internet connection... but the following equivalent written in OBJ-C is able to carry out the same task using essentially the same UI components found in Xcode's AppleScript ObjC interface designer.

Applescript:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

NSString *info = @"http://codeafterhours.wordpress.com";

// Generation of QR code image
NSData *qrCodeData = [info dataUsingEncoding:NSISOLatin1StringEncoding]; // recommended encoding
CIFilter *qrCodeFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrCodeFilter setValue:qrCodeData forKey:@"inputMessage"];
[qrCodeFilter setValue:@"M" forKey:@"inputCorrectionLevel"]; //default of L,M,Q & H modes

CIImage *qrCodeImage = qrCodeFilter.outputImage;

CGRect imageSize = CGRectIntegral(qrCodeImage.extent); // generated image size
CGSize outputSize = CGSizeMake(240.0, 240.0); // required image size
CIImage *imageByTransform = [qrCodeImage imageByApplyingTransform:CGAffineTransformMakeScale(outputSize.width/CGRectGetWidth(imageSize), outputSize.height/CGRectGetHeight(imageSize))];

UIImage *qrCodeImageByTransform = [UIImage imageWithCIImage:imageByTransform];
self.imgViewQRCode.image = qrCodeImageByTransform;

// Generation of bar code image
CIFilter *barCodeFilter = [CIFilter filterWithName:@"CICode128BarcodeGenerator"];
NSData *barCodeData = [info dataUsingEncoding:NSASCIIStringEncoding]; // recommended encoding
[barCodeFilter setValue:barCodeData forKey:@"inputMessage"];
[barCodeFilter setValue:[NSNumber numberWithFloat:7.0] forKey:@"inputQuietSpace"]; //default whitespace on sides of barcode

CIImage *barCodeImage = barCodeFilter.outputImage;
self.imgViewBarCode.image = [UIImage imageWithCIImage:barCodeImage];
}

Source: https://codeafterhours.wordpress.com/20 … es-in-ios/

I would like to be able to carry out the same function just with the AppleScriptObjC framework instead of the purely ObjC above.
The key emphasis is offline and without additional terminal tools that need to be installed first (i.e. link text gets converted to QR code and displayed in the NSImageView or similar).
As Google was not very helpful in pointing me in the right direction, I have my fingers crossed that the translation between the code above and its AppleScriptObjC equivalent is straightforward and something I can get a hand with (especially since ObjC is in the name of the target framework).

Thank you in advance.


Filed under: applescript, xcode, QR code

Offline

 

#2 2017-11-09 05:42:56 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: Generate QR code (offline)

Here's some code I've used. However, the scaling involves using CGAffineTransformMakeScale(), which I believe is not working from ASObjC in 10.13. You can use an alternative method of scaling instead.

Applescript:

use framework "Foundation"
use framework "AppKit"
use framework "CoreImage"
use scripting additions

set destPath to (POSIX path of (path to desktop folder)) & "Test QR Code.jpg"

my makeQRForString:"https://www.macosxautomation.com/applescript/apps/" ofWidth:360 savingTo:destPath

on makeQRForString:theString ofWidth:theWidth savingTo:posixPath
   set anImageFilter to current application's CIFilter's filterWithName:"CIQRCodeGenerator"
   anImageFilter's setDefaults()
   set thisURLString to current application's NSString's stringWithString:theString
   set thisData to thisURLString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
   anImageFilter's setValue:thisData forKey:"inputMessage"
   anImageFilter's setValue:"L" forKey:"inputCorrectionLevel"
   
   set baseImage to anImageFilter's outputImage()
   set baseImageWidth to baseImage's extent()'s |size|()'s width()
   set theScale to theWidth / baseImageWidth
   
   set thisTransform to current application's CGAffineTransform's CGAffineTransformMakeScale(theScale, theScale)
   set thisOutputImage to baseImage's imageByApplyingTransform:thisTransform
   set tiffData to thisOutputImage's TIFFRepresentation()
   tiffData's writeToFile:posixPath atomically:true
end makeQRForString:ofWidth:savingTo:


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2017-11-09 06:29:25 pm

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Re: Generate QR code (offline)

I have just found out that the CGAffineTransformMakeScale does indeed throw an error, so I found a rather basic method of scaling (which I call at the bottom of the makeQRForString function) that resizes but greatly blurs the output - that is where CGAffineTransformMakeScale would come in handy due to it's scaling operation before saving, not after.

Applescript:

on scaleImage(myPath)
   tell application "Image Events"
       set thisImage to open myPath
       scale thisImage to size 300
       save thisImage in myPath
       close thisImage
   end tell
end scaleImage

You can use an alternative method of scaling instead.

Could you give an example of an alternative method?

Whenever I try to set the NSImageView on the interface builder (imgView) using the following syntax, the error message NSImageCell's object value must be an NSImage. (error -10000) is thrown. Note: I am pulling from the tiffData variable, not the path to the saved QR code as I have omitted the saving part for now.

Applescript:

imgView's setImage_(tiffData)

Offline

 

#4 2017-11-09 09:45:47 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: Generate QR code (offline)

ProGrammer wrote:

Could you give an example of an alternative method?



This version uses bitmap image reps to do the job. Doing it this way also results in a greyscale file, rather than RGB:

Applescript:

use framework "Foundation"
use framework "AppKit"
use framework "CoreImage"
use scripting additions

set destPath to (POSIX path of (path to desktop folder)) & "Test QR Code.jpg"

my makeQRForString:"https://www.macosxautomation.com/applescript/apps/" ofWidth:360 savingTo:destPath

on makeQRForString:theString ofWidth:theWidth savingTo:posixPath
   set anImageFilter to current application's CIFilter's filterWithName:"CIQRCodeGenerator"
   anImageFilter's setDefaults()
   set thisURLString to current application's NSString's stringWithString:theString
   set thisData to thisURLString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
   anImageFilter's setValue:thisData forKey:"inputMessage"
   anImageFilter's setValue:"L" forKey:"inputCorrectionLevel"
   set baseImage to anImageFilter's outputImage()
   
   -- make image rep
   set imageRep to current application's NSBitmapImageRep's alloc()'s initWithCIImage:baseImage
   set theSize to imageRep's |size|()
   set actualWidth to current application's NSWidth(theSize)
   set actualHeight to current application's NSHeight(theSize)
   set theScale to theWidth / actualWidth
   
   -- make greyscale image rep
   set newRep to current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes:(missing value) pixelsWide:theWidth pixelsHigh:actualHeight * theScale bitsPerSample:8 samplesPerPixel:1 hasAlpha:false isPlanar:false colorSpaceName:(current application's NSCalibratedWhiteColorSpace) bytesPerRow:0 bitsPerPixel:0
   
   -- store graphics state and set new values
   current application's NSGraphicsContext's saveGraphicsState()
   set theContext to current application's NSGraphicsContext's graphicsContextWithBitmapImageRep:newRep
   current application's NSGraphicsContext's setCurrentContext:theContext
   theContext's setShouldAntialias:false
   theContext's setImageInterpolation:(current application's NSImageInterpolationNone)
   
   -- draw from original to new rep
   imageRep's drawInRect:(current application's NSMakeRect(0, 0, theWidth, actualHeight * theScale)) fromRect:(current application's NSZeroRect) operation:(current application's NSCompositeCopy) fraction:(1.0) respectFlipped:false hints:(missing value)
   
   -- restore state and save from new rep
   current application's NSGraphicsContext's restoreGraphicsState()
   set theProps to current application's NSDictionary's dictionaryWithObject:1.0 forKey:(current application's NSImageCompressionFactor)
   set imageData to (newRep's representationUsingType:(current application's NSTIFFFileType) |properties|:theProps)
   (imageData's writeToFile:posixPath atomically:true)
end makeQRForString:ofWidth:savingTo:

Last edited by Shane Stanley (2021-03-11 07:53:12 pm)


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#5 2017-11-10 01:26:16 am

ProGrammer
Member
Registered: 2016-12-16
Posts: 24

Re: Generate QR code (offline)

This version uses bitmap image reps to do the job. Doing it this way also results in a greyscale file, rather than RGB

You are so right! This is the way to go as the result is sharp and of very high quality.
The final part is drawing this image to into the image view instead of saving it to the file, I have just tried:

Applescript:

imgView's setImage:(imageData)

Which returns: NSImageCell's object value must be an NSImage. (error -10000) but isn't it already an NSImage? Is there a special way of translating / drawing this data into the NSImageView instead of the file? Cheers.

Offline

 

#6 2017-11-10 01:32:39 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: Generate QR code (offline)

You'll need to do something like:

Applescript:

set theImage to current application's NSImage's alloc()'s initWithData:imageData
imgView's setImage:theImage


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#7 2021-03-11 06:12:19 pm

Marc Anthony
Member
From:: Dallas, TX
Registered: 2006-04-27
Posts: 1017

Re: Generate QR code (offline)

Hi, Shane. The code in post #2 seems to have stopped working for me under Mojave. I would appreciate it if you could shed light on the returned error:

"{{0.0, 0.0}, {31.0, 31.0}} doesn’t understand the “size” message." number -1708 from {{0.0, 0.0}, {31.0, 31.0}} to «class size»

Offline

 

#8 2021-03-11 08:01:35 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6628

Re: Generate QR code (offline)

Marc,

I've edited the script. The issue is that NSRects used to be returned as records like {origin:{x:0, y:0}, size:{width:100, height:200}} but are now returned as lists of lists: {{0, 0}, {100, 200}}. The change I made will work in either case.

The change was made in High Sierra, and you can read more here:

https://latenightsw.com/high-sierra-app … objc-bugs/


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#9 2021-03-12 08:21:02 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 1797

Re: Generate QR code (offline)

Marc Anthony wrote:

Hi, Shane. The code in post #2 seems to have stopped working for me under Mojave. I would appreciate it if you could shed light on the returned error:

"{{0.0, 0.0}, {31.0, 31.0}} doesn’t understand the “size” message." number -1708 from {{0.0, 0.0}, {31.0, 31.0}} to «class size»


Shane Stanley edited post #4. I will help with correcting the post #2. Thanks for nice script. On Catalina it works:

Applescript:


use framework "Foundation"
use framework "AppKit"
use framework "CoreImage"
use scripting additions

set destPath to (POSIX path of (path to desktop folder)) & "Test QR Code.jpg"

my makeQRForString:"https://www.macosxautomation.com/applescript/apps/" ofWidth:360 savingTo:destPath

on makeQRForString:theString ofWidth:theWidth savingTo:posixPath
   set anImageFilter to current application's CIFilter's filterWithName:"CIQRCodeGenerator"
   anImageFilter's setDefaults()
   set thisURLString to current application's NSString's stringWithString:theString
   set thisData to thisURLString's dataUsingEncoding:(current application's NSUTF8StringEncoding)
   anImageFilter's setValue:thisData forKey:"inputMessage"
   anImageFilter's setValue:"L" forKey:"inputCorrectionLevel"
   
   set baseImage to anImageFilter's outputImage()
   set imageRep to current application's NSBitmapImageRep's alloc()'s initWithCIImage:baseImage
   set baseImageWidth to (imageRep's |size|())'s width()
   set theScale to theWidth / baseImageWidth
   
   set thisTransform to current application's CGAffineTransform's CGAffineTransformMakeScale(theScale, theScale)
   set thisOutputImage to baseImage's imageByApplyingTransform:thisTransform
   set tiffData to thisOutputImage's TIFFRepresentation()
   tiffData's writeToFile:posixPath atomically:true
end makeQRForString:ofWidth:savingTo:

For those of you wondering, the following script does the opposite:

Applescript:


-- Created 2018 by Takaaki Naganoya, Piyomaru Software
-- Adapted 25 Aug 2019 by me
use AppleScript version "2.4"
use framework "Foundation"
use framework "QuartzCore"
use scripting additions

property CIDetectorAccuracyHigh : a reference to CIDetectorAccuracyHigh of current application
property NSDictionary : a reference to NSDictionary of current application
property CIDetector : a reference to CIDetector of current application
property |NSURL| : a reference to |NSURL| of current application
property CIImage : a reference to CIImage of current application
property CIDetectorAccuracy : a reference to CIDetectorAccuracy of current application
property CIDetectorTypeQRCode : a reference to CIDetectorTypeQRCode of current application

-- Select image, Convert alias to URL, Generate CIImage
set imageFile to choose file of type {"public.image"}
set imageURL to |NSURL|'s fileURLWithPath:(POSIX path of imageFile)
set imageRef to CIImage's alloc()'s initWithContentsOfURL:imageURL

-- Create detector options with NSDictonary
set optionsDictionary to NSDictionary's dictionaryWithObject:(CIDetectorAccuracyHigh) forKey:(CIDetectorAccuracy)
set theDetector to CIDetector's detectorOfType:(CIDetectorTypeQRCode) context:(missing value) options:optionsDictionary

-- Perform QR code detection
set faceArray to theDetector's featuresInImage:imageRef options:optionsDictionary

-- Get the embeddedURLs of the detected QR code
set embeddedURLs to {}
repeat with theFace in faceArray
   set the end of embeddedURLs to (theFace's messageString()) as string
end repeat
embeddedURLs

Last edited by KniazidisR (2021-03-12 09:17:17 am)


Model: MacBook Pro
OS X: Catalina 10.15.4
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#10 2021-03-22 03:51:44 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 707

Re: Generate QR code (offline)

Here is a more compact version without repeat loop.

Applescript:

use AppleScript version "2.4"
use framework "Foundation"
use framework "QuartzCore"
use scripting additions

set theImage to choose file of type {"public.image"}

urlFromQR(theImage)

on urlFromQR(inputImage)
   set imageURL to current application's |NSURL|'s fileURLWithPath:(POSIX path of inputImage)
   set imageRef to current application's CIImage's alloc()'s initWithContentsOfURL:imageURL
   set optionsDictionary to current application's NSDictionary's dictionaryWithObject:"CIDetectorAccuracyHigh" forKey:"CIDetectorAccuracy"
   set theDetector to current application's CIDetector's detectorOfType:"CIDetectorTypeQRCode" context:(missing value) options:optionsDictionary
   set theFeature to theDetector's featuresInImage:imageRef options:optionsDictionary
   return (theFeature's firstObject())'s messageString() as string
end urlFromQR


if you are the expert, who will you call if its not your imagination.

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)