Skip to content

Commit

Permalink
Update readme and dependences
Browse files Browse the repository at this point in the history
  • Loading branch information
MatejGolian committed Oct 17, 2024
1 parent 898c9a0 commit 1b4bf54
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 29 deletions.
77 changes: 69 additions & 8 deletions Lib/OCR.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* OCR.FromWindow(WinTitle?, lang?, transform:=1, onlyClientArea:=0, mode:=4)
* OCR.FromFile(FileName, lang?, transform:=1)
* OCR.FromBitmap(bitmap, lang?, transform:=1, hDC?)
* OCR.FromPDF(FileName, lang?, transform:=1) => returns an array of results for each PDF page
* OCR.FromPDFPage(FileName, page:=1, lang?, transform:=1)
*
* Note: the first parameter of the OCR initiation methods may be an object mimicking named parameters.
* Eg. OCR.FromDesktop({lang:"en-us", grayscale:1})
Expand Down Expand Up @@ -94,6 +96,7 @@ class OCR {
, IID_IAsyncOperationCompletedHandler_OcrResult := "{989c1371-444a-5e7e-b197-9eaaf9d2829a}"
, IID_IAsyncOperationCompletedHandler_SoftwareBitmap := "{b699b653-33ed-5e2d-a75f-02bf90e32619}"
, IID_IAsyncOperationCompletedHandler_BitmapDecoder := "{bb6514f2-3cfb-566f-82bc-60aabd302d53}"
, IID_IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}"
, Vtbl_GetDecoder := {bmp:6, jpg:7, jpeg:7, png:8, tiff:9, gif:10, jpegxr:11, ico:12}
, PerformanceMode := 0
, DisplayImage := 0
Expand Down Expand Up @@ -504,7 +507,7 @@ class OCR {
If IsSet(searchArea) && (word.x < x1 || word.y < y1 || word.x+word.w > x2 || word.y+word.h > y2)
continue
t := word.Text, len := StrLen(t)
if wordCompareFunc(splitNeedle[found.Length+1], t) {
if wordCompareFunc(t, splitNeedle[found.Length+1]) {
found.Push(word)
if found.Length == needleLen {
if ++counter == i {
Expand Down Expand Up @@ -705,21 +708,79 @@ class OCR {
/**
* Returns an OCR results object for an image file. Locations of the words will be relative to
* the top left corner of the image.
* @param FileName Either full or relative (to A_ScriptDir) path to the file.
* @param FileName Either full or relative (to A_WorkingDir) path to the file.
* @param lang OCR language. Default is first from available languages.
* @param transform Either a scale factor number, or an object {scale:Integer, grayscale:Boolean, invertcolors:Boolean}
* @returns {OCR.OcrResult}
*/
static FromFile(FileName, lang?, transform:=1) {
this.__ExtractTransformParameters(FileName, &transform)
this.__ExtractNamedParameters(FileName, "lang", &lang, "scale", &scale, "FileName", &FileName)
if (SubStr(FileName, 2, 1) != ":")
FileName := A_ScriptDir "\" FileName
if !(fe := FileExist(FileName)) or InStr(fe, "D")
if !(fe := FileExist(FileName)) or InStr(fe, "D")
throw TargetError("File `"" FileName "`" doesn't exist", -1)
GUID := this.CLSIDFromString(this.IID_IRandomAccessStream)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", FileName, "uint", Read := 0, "ptr", GUID, "ptr*", IRandomAccessStream:=this.IBase())
return this(IRandomAccessStream, lang?, transform, this.Vtbl_GetDecoder.HasOwnProp(ext := StrSplit(FileName, ".")[-1]) ? ext : "")
}

/**
* Returns an array of OCR results objects for a PDF file. Locations of the words will be relative to
* the top left corner of the PDF page.
* @param FileName Either full or relative (to A_WorkingDir) path to the file.
* @param lang OCR language. Default is first from available languages.
* @param transform Either a scale factor number, or an object {scale:Integer, grayscale:Boolean, invertcolors:Boolean}
* @returns {OCR.OcrResult}
*/
static FromPDF(FileName, lang?, transform:=1) {
this.__ExtractTransformParameters(FileName, &transform)
this.__ExtractNamedParameters(FileName, "lang", &lang, "scale", &scale, "FileName", &FileName)
if !(fe := FileExist(FileName)) or InStr(fe, "D")
throw TargetError("File `"" FileName "`" doesn't exist", -1)
GUID := this.CLSIDFromString(this.IID_IRandomAccessStream)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", FileName, "uint", Read := 0, "ptr", GUID, "ptr*", IRandomAccessStream:=this.IBase())
return this(IRandomAccessStream, lang?, transform, this.Vtbl_GetDecoder.HasOwnProp(ext := StrSplit(FileName, ".")[-1]) ? ext : "")

DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", FileName, "uint", Read := 0, "ptr", GUID := this.CLSIDFromString(this.IID_IRandomAccessStream), "ptr*", IRandomAccessStream:=ComValue(13,0))
PdfDocumentStatics := this.CreateClass("Windows.Data.Pdf.PdfDocument", this.IID_IPdfDocumentStatics) ; If this line is removed then the script throws an error for a second on script exist
ComCall(8, PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument:=this.IBase()) ; LoadFromStreamAsync
this.WaitForAsync(&PdfDocument)
this.CloseIClosable(IRandomAccessStream)
ComCall(7, PdfDocument, "uint*", &count:=0) ; GetPageCount
if !count
throw Error("Unable to get PDF page count", -1)
results := []
Loop count
results.Push(this.FromPDFPage(PdfDocument, A_Index, lang?, transform:=1))
return results
}

/**
* Returns an OCR result object for a PDF page. Locations of the words will be relative to
* the top left corner of the PDF page.
* @param FileName Either full or relative (to A_WorkingDir) path to the file.
* @param Page The page number to OCR. Default is 1.
* @param lang OCR language. Default is first from available languages.
* @param transform Either a scale factor number, or an object {scale:Integer, grayscale:Boolean, invertcolors:Boolean}
* @returns {OCR.OcrResult}
*/
static FromPDFPage(FileName, page:=1, lang?, transform:=1) {
this.__ExtractTransformParameters(FileName, &transform)
this.__ExtractNamedParameters(FileName, "page", page, "lang", &lang, "scale", &scale, "FileName", &FileName)
if FileName is String {
if !(fe := FileExist(FileName)) or InStr(fe, "D")
throw TargetError("File `"" FileName "`" doesn't exist", -1)
GUID := OCR.CLSIDFromString(OCR.IID_IRandomAccessStream)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", FileName, "uint", Read := 0, "ptr", GUID, "ptr*", IRandomAccessStream:=OCR.IBase())
PdfDocumentStatics := this.CreateClass("Windows.Data.Pdf.PdfDocument", this.IID_IPdfDocumentStatics)
ComCall(8, PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument:=this.IBase()) ; LoadFromStreamAsync
this.WaitForAsync(&PdfDocument)
} else
PdfDocument := FileName
ComCall(6, PdfDocument, "uint", page-1, "ptr*", PdfPage:=this.IBase()) ; GetPage
InMemoryRandomAccessStream := this.CreateClass("Windows.Storage.Streams.InMemoryRandomAccessStream")
ComCall(6, PdfPage, "ptr", InMemoryRandomAccessStream, "ptr*", asyncInfo:=this.IBase()) ; RenderToStreamAsync
this.WaitForAsync(&asyncInfo)
if FileName is String
this.CloseIClosable(IRandomAccessStream)
PdfPage := "", PdfDocument := "", IRandomAccessStream := ""
return this(InMemoryRandomAccessStream, lang?, transform)
}

/**
Expand Down
44 changes: 24 additions & 20 deletions Lib/UIA.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ static __CreateRawCondition(condition) {
return condition
}

static __ConditionBuilder(obj, &nonUIAEncountered?) {
static __ConditionBuilder(obj, &nonUIAEncountered?, isParentNotCondition:=0) {
local sanitizeMM, operator, cs, mm, flags, count, k, v, t, i, j, val, match, arr
sanitizeMM := False
switch Type(obj) {
Expand Down Expand Up @@ -750,14 +750,14 @@ static __ConditionBuilder(obj, &nonUIAEncountered?) {
v += 50000
}
if sanitizeMM && this.PropertyVariantTypeBSTR.Has(k) {
t := mm = 1 ? this.CreateCondition(k, v, !cs | 2) : this.CreateNotCondition(this.CreatePropertyCondition(k, ""))
t := mm = 1 ? this.CreateCondition(k, v, !cs | 2) : (isParentNotCondition ? this.CreatePropertyCondition(k, "") : this.CreateNotCondition(this.CreatePropertyCondition(k, "")))
arr[i++] := t[]
} else if (k >= 30000) {
t := this.CreateCondition(k, v, flags)
arr[i++] := t[]
}
} else if IsObject(v) && !(SubStr(Type(v), 1, 6) = "ComObj") && !v.HasOwnProp("ptr") {
t := this.__ConditionBuilder(v, &nonUIAEncountered)
t := this.__ConditionBuilder(v, &nonUIAEncountered, isParentNotCondition ^ (k = "not" || operator = "not"))
if k = "not" || operator = "not"
t := this.CreateNotCondition(t)
arr[i++] := t[]
Expand Down Expand Up @@ -2377,9 +2377,9 @@ class IUIAutomationElement extends UIA.IUIAutomationBase {

levelCondition := levelConditions[targetIndex]
if asString
conditionPath .= (levelCondition[1].i = levelMap[levelCondition[2]] ? (levelCondition[1].i = 1 ? levelCondition[2] : (levelCondition[1].i := -1, levelCondition[2] ", i:-1")) : levelCondition[2] ", i:" levelCondition[1].i) "}" (A_Index = numPath.Length ? "" : ", ")
conditionPath .= ((levelCondition[1].i = 1) ? levelCondition[2] : (levelCondition[1].i = levelMap[levelCondition[2]] ? (levelCondition[1].i := -1, levelCondition[2] ", i:-1") : (levelCondition[2] ", i:" levelCondition[1].i))) "}" (A_Index = numPath.Length ? "" : ", ")
else
conditionPath.Push(levelCondition[1].i = levelMap[levelCondition[2]] ? (levelCondition[1].i = 1 ? (levelCondition[1].DeleteProp("i"), levelCondition[1]) : (levelCondition[1].i := -1, levelCondition[1])) : levelCondition[1])
conditionPath.Push((levelCondition[1].i = 1) ? (levelCondition[1].DeleteProp("i"), levelCondition[1]) : (levelCondition[1].i = levelMap[levelCondition[2]] ? (levelCondition[1].i := -1, levelCondition[1]) : levelCondition[1]))
cachedThis := children[targetIndex]
}
return conditionPath
Expand Down Expand Up @@ -3548,14 +3548,14 @@ class IUIAutomationElement extends UIA.IUIAutomationBase {

; Internal method: checks whether this element matches the condition
ValidateCondition(cond, cached:=False) {
local mm := 3, cs := 1, notCond := 0, k, v, result, i, val
local mm, cs, notCond := 0, k, v, result, i, val
switch Type(cond) {
case "Object":
mm := cond.HasOwnProp("matchmode") ? cond.matchmode : cond.HasOwnProp("mm") ? cond.mm : 3
cs := cond.HasOwnProp("casesense") ? cond.casesense : cond.HasOwnProp("cs") ? cond.cs : 1
if !IsInteger(mm)
mm := cond.HasOwnProp("matchmode") ? cond.matchmode : cond.HasOwnProp("mm") ? cond.mm : unset
cs := cond.HasOwnProp("casesense") ? cond.casesense : cond.HasOwnProp("cs") ? cond.cs : unset
if !IsInteger(mm ?? 0)
mm := UIA.MatchMode.%mm%
if !IsInteger(cs)
if !IsInteger(cs ?? 0)
cs := UIA.CaseSense.%cs%
notCond := cond.HasOwnProp("operator") ? cond.operator = "not" : cond.HasOwnProp("op") ? cond.op = "not" : 0
cond := cond.OwnProps()
Expand Down Expand Up @@ -3587,11 +3587,11 @@ class IUIAutomationElement extends UIA.IUIAutomationBase {
case 8:
if v is Array {
for val in v {
if (result := CompareStrings(currentValue, val, mm, cs))
if (result := CompareStrings(currentValue, val, mm?, cs?))
break
}
} else
result := CompareStrings(currentValue, v, mm, cs)
result := CompareStrings(currentValue, v, mm?, cs?)
case 3,5,11:
if v is Array {
for val in v {
Expand Down Expand Up @@ -3630,19 +3630,23 @@ class IUIAutomationElement extends UIA.IUIAutomationBase {
}
return True

CompareStrings(str1, str2, matchmode, casesense) {
CompareStrings(str1, str2, matchmode:=3, casesense:=-1) {
if not str1 is String
str1 := String(str1)
if not str2 is String
str2 := String(str2)
if matchmode = "RegEx"
return RegExMatch(str1, str2)
else if matchmode = 1
return ((casesense && SubStr(str1, 1, StrLen(str2)) == str2) || (!casesense && SubStr(str1, 1, StrLen(str2)) = str2))
else if matchmode = 2
return InStr(str1, str2, casesense)
else
if matchmode = 3
return (casesense ? str1 == str2 : str1 = str2)
else if matchmode = 2
return InStr(str1, str2, !!casesense)
if matchmode = 1
return ((casesense && SubStr(str1, 1, StrLen(str2)) == str2) || (!casesense && SubStr(str1, 1, StrLen(str2)) = str2))
else if matchmode = "RegEx" {
if casesense = -1
return RegExMatch(str1, str2)
return RegExMatch(str1, (!casesense && !RegExMatch(str2, "^([^(\\]+)\)")) ? "i)" str2 : str2)
} else
throw Error("Invalid MatchMode", -1, matchmode)
}
}

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ Our changelog can be found [in our dedicated changelog file](https://github.com/
## Known Issues

* The ReaHotkey script can crash while In Dubler 2 audio calibration view. In that case just start the script again.
* Using any other zoom value in Kontakt than 100% may result in ReaHotkey not operating correctly.
* Using any other zoom value in Kontakt than 100% may result in ReaHotkey not operating as excepted.
* At present library-specific overlays may not always behave correctly when used directly in the Kontakt 8 plug-in. In such case try using Kontakt 7 or open the given library from within Komplete Kontrol instead.
* Due to OCR limitations the Komplete Kontrol Save Preset dialog may display incorrect preset names at times.

## <a name="dubler2"></a>Dubler 2 Accessibility
Expand Down

0 comments on commit 1b4bf54

Please sign in to comment.