In fact Shane's fear was right.
The described process store a bitmap file.
When Preview opens it, it is correctly displayed. Alas, there is an annoying feature.
It is stored as an horizontal symmetry of the correct picture.
Preview and other apps are able to display it correctly because in the file header, the four bytes supposed to contain the height of the picture contain (stored backwards):
$FFFFFF9E (which is $100000000-$00000062) and the line descriptors are stored in the reversed order of what is got when the file is created as PNG then converted into BMP in SketchBookPro.app.
I decided to use GUIScriping to force SketchBookPro to do the conversion.
Here is the full script, not a pretty one, but it do what it's written to achieve.
use AppleScript version "2.4"
use framework "Foundation"
use framework "ASObjCExtras"
use scripting additions
script o
property fakeList : {}
end script
set {dName, sName, tName, cellName} to my get_SelParams()
tell application id "com.apple.iWork.Numbers" to tell document dName to tell sheet sName to tell table tName
set selection range to range cellName
end tell
set the clipboard to "" # guarantee that there is no "public.tiff" data in the clipboard
tell application "System Events" to tell process "Numbers"
set frontmost to true
keystroke "c" using {command down}
end tell
my wait4cells()
set bareName to "Cellule extraite" --"Grabbed Cell" --
set p2d to path to desktop as text
set extractedTiffFile to p2d & bareName & ".tiff"
set nameOfBmp to bareName & ".bmp"
set extractedBmpFile to p2d & nameOfBmp
set nameOfCroppedBmp to "cropped.bmp"
set theCroppedBmp to p2d & nameOfCroppedBmp
tell application "System Events"
if exists disk item extractedTiffFile then delete disk item extractedTiffFile
if exists disk item extractedBmpFile then delete disk item extractedBmpFile
if exists disk item theCroppedBmp then delete disk item theCroppedBmp
end tell
set extractedFile to POSIX path of extractedTiffFile
my fileFromClipToPath:extractedFile
my waitForSaveEnd(extractedTiffFile)
#===== Convert the Png into bmp with SketchBookPro
tell application "SketchBookPro" to open (extractedTiffFile as alias)
tell application "System Events" to tell process "SketchBookPro"
set frontmost to true
keystroke "s" using {command down, shift down}
properties of every window
repeat
if exists (first window whose subrole is "AXDialog") then exit repeat
delay 0.1
end repeat
log (get subrole of every window)
tell (first window whose subrole is "AXDialog")
keystroke "d" using {command down} # To store the file on the desktop
tell group 1
--class of UI elements --> {button, pop up button, static text}
set FormatMenu to first pop up button
click FormatMenu
name of menu items of menu 1 of FormatMenu
--> {"TIFF", "JPEG", "PNG", "BMP", "Adobe Photoshop (PSD)", "Pixlr PXD"}
click menu item 4 of menu 1 of FormatMenu
end tell # group 1
class of UI elements --> {group, radio group, group, pop up button, text field, splitter group}
set value of first text field to nameOfBmp
end tell
keystroke return
end tell
# I leavethis attempt to wait for the end of Save process but it doesn't do its duty.
my waitForSaveEnd(POSIX path of extractedBmpFile)
# ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? #
#
# I don't understand why but these old instructions fail on read file when there is the "use scripting additions" line
(*
set pathToPng to (p2d & "cellule:grabbed Cell.bmp")
set theDatas to read file pathToPng as «class data»
*)
# ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? #
repeat
try
set pathToBmp to extractedBmpFile as alias
exit repeat # The bmp file is available
on error
delay 0.1
end try
end repeat
tell application "SketchBookPro" to quit
using terms from application "SketchBookPro" # Or every application understanding "read"
set theDatas to read pathToBmp as «class data»
end using terms from
try
theDatas as text
on error errMsg
set theDatas to errMsg
end try
set theDatas to item 2 of my decoupe(theDatas, {"«data rdat", "«data data", "»"})
--log theDatas
set begOffset to (11 * 2) - 1
set offsetHex to text begOffset thru (begOffset - 1 + 4 * 2) of theDatas
--> "360000000"
set offsetDeci to my hexToDec8(offsetHex)
log "offsetDeci : " & offsetDeci --> 54
set begWidth to (19 * 2) - 1
set widthHex to text begWidth thru (begWidth - 1 + 4 * 2) of theDatas
--> "18010000"
set widthDeci to my hexToDec8(widthHex)
log "widthDeci : " & widthDeci --> 280
set begHeight to (23 * 2) - 1
set heightHex to text begHeight thru (begHeight - 1 + 4 * 2) of theDatas
--> "62000000"
set heightDeci to my hexToDec8(heightHex)
log "heightDeci : " & heightDeci --> 98
set headerLength to offsetDeci * 2
set originalHeader to text 1 thru headerLength of theDatas
log "headerLength : " & headerLength
log "originalHeader : " & originalHeader
# ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? #
#
# Now, we have the header in hexadecimal form,
# We may extract the block of pixels descriptors - 3 bytes per pixel - as old fashion so called High ASCII characters.
# It works with French settings, would do in English but I have no idea of its behaviour with Eastern languages.
#
# ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? # ? #
set theDatas to read pathToBmp as text
set pixelBlock to text (offsetDeci + 1) thru -1 of theDatas
# A pixel is described by 3 bytes which means 3 characters here
# CAUTION. The file may be built with padding bytes at the end of lines because the count of bytes per line must be a multiple of 4
set cntVerticals3 to widthDeci * 3
if cntVerticals3 / 4 = cntVerticals3 div 4 then
set realLength to cntVerticals3
else
set realLength to ((1 + cntVerticals3 div 4) * 4)
end if
set theLines to {}
repeat with lineNum from 1 to heightDeci
set end of theLines to text (1 + (lineNum - 1) * realLength) thru ((lineNum - 1) * realLength + cntVerticals3) of pixelBlock
end repeat
# CAUTION, the lines are stored starting with the lower one
# Scan the contents of bitmap lines
set lineHorizontalFrame to ""
set lineIndex to heightDeci
set blankLine to theLines's item lineIndex
repeat with lineIndx from lineIndex to 1 by -1
log "lineIndx : " & lineIndx
log theLines's item lineIndx
if theLines's item lineIndx ≠ blankLine then exit repeat
end repeat
# Here we reached the first line displaying the title : "Tableau 1" in French
repeat with lineIndex from lineIndx to 1 by -1
log "lineIndex : " & lineIndex
log theLines's item lineIndex
if theLines's item lineIndex = blankLine then exit repeat
end repeat
# Here we reached the first line below the title : "Tableau 1" in French
repeat with lineIndx from lineIndex to 1 by -1
log "lineIndx : " & lineIndx
log theLines's item lineIndx
if theLines's item lineIndx ≠ blankLine then exit repeat
end repeat
# Here we reached the upper line of the rectangle enclosing the target
set horizontalFrame to theLines's item lineIndx
set lineIndx to lineIndx - 1
set blankInFrame to theLines's item lineIndx
repeat with lineIndex from lineIndx to 1 by -1
if theLines's item lineIndex ≠ blankInFrame then exit repeat
end repeat
# Here we reached the first line to keep
set lowerLineToKeep to lineIndex
-- log "lowerLineToKeep : " & lineIndex
set removeAtTop to lowerLineToKeep - 1
repeat with lineIndx from lineIndex to 1 by -1
if theLines's item lineIndx = blankInFrame then exit repeat
end repeat
# Here we reached the 1st line above our target
set removeAtBottom to lineIndx
set upperLineToKeep to lineIndx + 1
-- log "lineIndx : " & lineIndx
set theLines to items upperLineToKeep thru lowerLineToKeep of theLines
set cntLines to (count theLines)
# Build lists of groups of 3 bytes describing a pixel
repeat with i from 1 to cntLines
set lineAsText to item i of theLines
set lineAsList to {}
repeat with j from 1 to widthDeci
set end of lineAsList to text ((j * 3) - 2) thru (j * 3) of lineAsText
end repeat
set item i of theLines to lineAsList
end repeat
# swap columns to rows
set theVerticals to (current application's SMSFord's colsToRowsIn:theLines |error|:(missing value)) as list
set cntVerticals to count theVerticals
set blankVertical to item 1 of theVerticals
repeat with i from 1 to cntVerticals
if item i of theVerticals ≠ blankVertical then exit repeat
end repeat
# here we found the border of the frame
repeat with j from i to cntVerticals
if item j of theVerticals = blankVertical then exit repeat
end repeat
# Here we are in what remains of the frame
repeat with i from j to cntVerticals
if item i of theVerticals ≠ blankVertical then exit repeat
end repeat
# Here we reached the first line to keep
set firstVerticalToKeep to i
set removeAtRight to firstVerticalToKeep - 1
repeat with i from cntVerticals to 1 by -1
if item i of theVerticals ≠ blankVertical then exit repeat
end repeat
# Here we reached the other side of the frame
repeat with j from i to 1 by -1
if item j of theVerticals = blankVertical then exit repeat
end repeat
# Here we are in what remains of the frame
repeat with i from j to 1 by -1
if item i of theVerticals ≠ blankVertical then exit repeat
end repeat
#Here we reached the last line to keep
set lastVerticalToKeep to i
set removeAtLeft to cntVerticals - lastVerticalToKeep
--log "firstVerticalToKeep : " & firstVerticalToKeep
--log "lastVerticalToKeep : " & lastVerticalToKeep
set theVerticals to items firstVerticalToKeep thru lastVerticalToKeep of theVerticals
set cntVerticals to count theVerticals
--theVerticals
# swap back columns to rows
set theLines to (current application's SMSFord's colsToRowsIn:theVerticals |error|:(missing value)) as list
# CAUTION: the number of bytes describing a line must be a multiple of 4
set cntVerticals3 to cntVerticals * 3
if cntVerticals3 / 4 = cntVerticals3 div 4 then
set pad to ""
else
set added to ((1 + cntVerticals3 div 4) * 4) - cntVerticals3
set pad to text 1 thru added of (character id 0 & character id 0 & character id 0)
end if
repeat with i from 1 to count theLines
set item i of theLines to my recolle(item i of theLines, "") & pad
end repeat
set cntLines to count theLines
set newBlock to my recolle(theLines, "")
set fullsize to offsetDeci + (count newBlock)
--log "cntVerticals : " & cntVerticals
--log "cntLines : " & cntLines
--log "fullsize : " & fullsize
--newBlock
# Adjust the header
# Convert the decimal fullsize into hex (LO byte first)
set fullSizeHex to my num2Reversed4Bytes(fullsize)
# Convert the decimal width into hex (LO byte first)
set widthHex to my num2Reversed4Bytes(cntVerticals)
# Convert the decimal height into hex (LO byte first)
set heightHex to my num2Reversed4Bytes(cntLines)
# Build the new header
set newHeader to text 1 thru 4 of originalHeader & fullSizeHex & text 13 thru 36 of originalHeader & widthHex & heightHex & text 53 thru -1 of originalHeader
--log (count originalHeader)
--log (count newHeader)
--log originalHeader
--log newHeader
# I dislike the use of ASCII character but at this time I have no alternate scheme to write the edited header.
set o's fakeList to {}
repeat with i from 1 to ((count newHeader) - 1) by 2
set end of o's fakeList to (ASCII character my hex2num(text i thru (i + 1) of newHeader))
end repeat
set fakeText to my recolle(o's fakeList, "")
tell application "System Events"
make new file at end of folder p2d with properties {name:nameOfCroppedBmp}
end tell
write fakeText & newBlock to (theCroppedBmp as alias)
tell application "System Events"
delete disk item extractedTiffFile
delete disk item extractedBmpFile
end tell
#===== ===== ===== ===== ===== ===== =====
on decoupe(t, d)
local oTIDs, l
set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
set l to text items of t
set AppleScript's text item delimiters to oTIDs
return l
end decoupe
#=====
on recolle(l, d)
local oTIDs, t
set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
set t to l as text
set AppleScript's text item delimiters to oTIDs
return t
end recolle
#=====
on num2hex(n)
return (character (1 + n div 16) of "0123456789ABCDEF") & (character (1 + n mod 16) of "0123456789ABCDEF")
end num2hex
#=====
on num2Reversed4Bytes(n)
set w4 to n div (16 ^ 6)
set r4 to n - (w4 * (16 ^ 6))
set w3 to r4 div (16 ^ 4)
set r3 to r4 - (w3 * (16 ^ 4))
set w2 to r3 div (16 ^ 2)
set r2 to r3 - (w2 * (16 ^ 2))
return my num2hex(r2) & my num2hex(w2) & my num2hex(w3) & my num2hex(w4)
end num2Reversed4Bytes
#=====
on hex2num(t)
return ((offset of (text item 1 of t) in "0123456789ABCDEF") - 1) * 16 + (offset of (text item 2 of t) in "0123456789ABCDEF") - 1
end hex2num
#=====
on hexToDec8(txt)
set deci to 0
repeat with i from 6 to 0 by -2
set deci to (deci * 256) + (my hex2num(text (i + 1) thru (i + 2) of txt))
end repeat
return deci
end hexToDec8
#=====
# Test built upon the one written by Shane STANLEY
on wait4cells()
local pb, isOK, i
set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
repeat with i from 1 to 20 -- some limit
tell current application to delay 0.5
if (pb's types()'s containsObject:"public.tiff") = 1 then exit repeat
if i = 20 then
error "No text data available in the clipboard !"
end if
end repeat
end wait4cells
#=====
# Handler built upon the one written by Shane STANLEY
on fileFromClipToPath:thePath
set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
set theType to pb's availableTypeFromArray:{current application's NSPasteboardTypeTIFF}
if the theType is missing value then error "No suitable image data found on the clipboard"
-- log theType as text
set theData to pb's dataForType:theType
if thePath ends with ".tiff" then
-- no change required, save tiff data as is
else if thePath ends with ".png" then # Convert the Tiff datas into Png ones
set newRep to current application's NSBitmapImageRep's imageRepWithData:theData
set theData to (newRep's representationUsingType:(current application's NSPNGFileType) |properties|:{NSTIFFCompressionNone:1})
else if (thePath ends with ".jpg") or thePath ends with ".jpeg" then # Convert the Tiff datas into Jpeg ones
set compFactor to 1
set newRep to current application's NSBitmapImageRep's imageRepWithData:theData
set theData to (newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compFactor, NSImageProgressive:false})
else if (thePath ends with ".bmp") then # Convert the Tiff datas into Bmp ones
set newRep to current application's NSBitmapImageRep's imageRepWithData:theData
set theData to (newRep's representationUsingType:(current application's NSBMPFileType) |properties|:{NSImageProgressive:false})
else # No extension given, save as Tiff
set thePath to my ChangeExtension(thePath, "tiff") as text
end if
set theResult to (theData's writeToFile:thePath atomically:true)
return (theResult = 1)
end fileFromClipToPath:
#=====
# I don't remember which one but this handler was probably built upon one written by Shane STANLEY
on ChangeExtension(thePath, newExt)
set pathNSString to current application's NSString's stringWithString:thePath
return pathNSString's stringByDeletingPathExtension()'s stringByAppendingPathExtension:newExt
end ChangeExtension
#=====
(*
set { dName, sName, tName, cellName} to my get_SelParams()
tell application "Numbers" to tell document dName to tell sheet sName to tell table tName
*)
on get_SelParams()
try
tell application id "com.apple.iWork.Numbers"
set t to front document's active sheet's first table whose selection range's class is range
return {front document's name, t's parent's name, t's name, name of first cell of t's selection range}
end tell
on error
display dialog "Problem getting values. Did you select cells?" buttons "Cancel" cancel button 1
end try
end get_SelParams
#=====
on waitForSaveEnd(POSIXPath)
# check that the file is available
set theNSFileManager to current application's NSFileManager's defaultManager()
if (theNSFileManager's fileExistsAtPath:POSIXPath) as boolean is false then return false
set aURL to current application's class "NSURL"'s fileURLWithPath:POSIXPath
set oldSize to -1
set valuesList to {current application's NSURLFileSizeKey, current application's NSURLIsDirectoryKey}
set attsNSDictionary to (aURL's resourceValuesForKeys:valuesList |error|:(missing value))
set itsAFolderOrAPackage to (attsNSDictionary's valueForKey:(current application's NSURLIsDirectoryKey)) as boolean
# Wait until the size no longer change
repeat
if itsAFolderOrAPackage then
# It's a folder, call a recursive dedicated handler
set newSize to my folderSizeFor:aURL rawSize:0
else
set newSize to ((attsNSDictionary's valueForKey:(current application's NSURLFileSizeKey)) as integer)
end if
if newSize = oldSize then return true
set oldSize to newSize
end repeat
end waitForSaveEnd
#=====