ahk1开发的爬取搜狗网页并获取结果的ocr工具,同时具备翻译功能,可以很好的对不同语言的识别并翻译,包括汉语,日语,英语,汉语,繁体等进行识别和翻译,总之参考搜狗网页介绍。
1、背景
由于在写搜狗翻译本地化的时候无意看到还有ocr,就顺带一起嫖过来了、
搜狗:年轻人耗子尾汁…
2、实现原理
调试搜狗所有网页发现必需获取特定的cookie和查看js代码中的部分算法才能上传图片,先请求三个地址获取cookie然后用ahk实现js的算法补全参数,调用上传地址上传图片获取返回的json ,然后解析json渲染到activeX控件中。
- 截图实现:使用snipast 截图 (优点截图功能强大)
- 上传实现:调用ctrl+c (snipast截图快捷键)把图片存到粘贴板,通过GDI+把图片处理后存为png文件有些过小的图片需要缩放
- post请求:ADODB.Stream来实现post的二进制上传
- 解析json:利用js的eval函数
- 显示:gui的activeX显示html文件,依旧使用扣取html的方法,掏空body把结果组成的标签放进去,剩下的就交给搜狗网页的兼容了。
优点:
- 不需依赖外部ahk文件或第三方文件,和注册三方云接口来实现。
- 可以识别很多语言包括中日韩英等,同时除了中文外的其它语言还可以得到翻译后的中文。
- 界面美观简洁,翻译结果准确率高。
缺点:
- 需要联网才能实现这些功能
- 由于高度依赖搜狗网站,如果搜狗网站结构发生或者代码发生变化可能导致无法使用。
- 第一次使用时要访问3个不必要网址速度慢,后面会缓存结果,只访问一个网址即可得到结果速度会提升。
3、使用方法
- 必须安装snipast 并且具备快捷键ctrl+c保存图(默认就是)
- 在snipast选中区域时按ctrl+1快捷键就可以识别图片(快捷键可自己在代码中修改,最好不要和snipast的冲突)
tip:其实自己也可以用gdi+实现屏幕截图,后来详想想没啥必要。一是比较麻烦代码量会增大,二是snipast确实很强大用了就不想用其它截图了。
#Persistent #SingleInstance #InstallMouseHook #InstallKeybdHook #KeyHistory 499 ; 强制使用32位运行 if !(A_IsUnicode=1 and A_PtrSize=4) { SplitPath, A_AhkPath, , dir Run, %dir%\AutoHotkeyU32.exe "%A_ScriptFullPath%" ExitApp } CoordMode,Mouse ;全局获取模式 OnMessage(0x100 , "keyboard_message_callback") ;键盘事件 Return ; F1键调用搜狗OCR剪贴板的图片内容 F1::SoGouOcr.show() ;只对Snipast生效 #IfWinActive Snipper - Snipaste $^1:: SoGouOcr.show() return ; #if ;移动选框 WM_LBUTTONDOWN(){ Static init:=OnMessage(0x0201, "WM_LBUTTONDOWN") if(A_Cursor="Arrow") PostMessage, 0xA1, 2 return ; } ;键盘事件的回调函数 keyboard_message_callback(wparam,lparma,msg) { WinGetActiveTitle, active_title ; tooltip ,% active_title ":" wParam if((wparam=13 || wparam=27)) ;响应回车事件和esc事件 { Gui sgocr:destroy } } ;复制原文 srcCopyButton1_OnClick() { ; msgBox % SoGouOcr.result_source " :" SoGouOcr.result_trans clipboard:=SoGouOcr.result_source Gui sgocr:destroy } ;复制翻译后 srcCopyButton2_OnClick() { ; msgBox % SoGouOcr.result_source " :" SoGouOcr.result_trans clipboard:=SoGouOcr.result_trans Gui sgocr:destroy } ;关闭窗口事件 exitBtn1_OnClick() { ; msgBox click Gui sgocr:destroy } ;ico点击动画 label_ico_click_animate: { SoGouTrans.animate_timer_count:=SoGouTrans.animate_timer_count+1 if(SoGouTrans.html_ready || SoGouTrans.ico_new|| SoGouTrans.animate_timer_count*SoGouTrans.animate_timer_gap>SoGouTrans.animate_out_time) { setTimer ,label_ico_click_animate,off ; 关闭定时器 GuiControl,translate:hide ,Ico_wait_text1 ;停止显示动画 GuiControl,translate:hide ,Ico_wait_text2 ;停止显示动画 GuiControl,translate:hide ,Ico_wait_text3 ;停止显示动画 return } ico_wait_shape :=Util.decodeUtf8(SoGouTrans.ico_wait_shape) ico_wait_shape :=SoGouTrans.ico_wait_shape ico_size:=SoGouTrans.ico_size wait1_offset_x:=1.5*ico_size wait2_offset_x:=2*ico_size wait3_offset_x:=2.5*ico_size ico_color:=SoGouTrans.ico_color i:=SoGouTrans.animate_timer_count ;INDEX 从1开始 if(i=1) { global Ico_wait_text1 global Ico_wait_text2 global Ico_wait_text3 } if(i<=3) { offset_x:=(0.46*i+1)*ico_size-SoGouTrans.point_to_ico Gui ,translate:Add, Text, vIco_wait_text%i% x%offset_x% y0 c%ico_color%, % ico_wait_shape ; label_gui_event标签处理gui事件 }else { loop ,% mod(i,4) ;显示 { ; tooltip loop1=Ico_wait_text%A_index% GuiControl,translate:show ,Ico_wait_text%A_index% } loop ,% 3-(mod(i,4)) ;隐藏 { hide_index:=4-A_index GuiControl,translate:hide ,Ico_wait_text%hide_index% } } ; MouseGetPos ,x ,y ; toolTip % "x:" x " y:" y return } ;延时后去掉ico label_destroy_ico: { SoGouTrans.ico_timer_count:=SoGouTrans.ico_timer_count+1 IfWinNotExist ,% SoGouTrans.html_title { if(SoGouTrans.ico_timer_count*50>=SoGouTrans.ico_destroy_time) { Gui, Translate:destroy setTimer ,label_destroy_ico,off setTimer, label_ico_click_animate ,off } } return ; } ;gui事件处理 https://ahkcn.sourceforge.net/docs/Variables.htm#GuiEvent label_gui_event: { if(A_GuiControl="Ico_text" && A_GuiControlEvent="Normal") ;点击翻译图标时触发 { ;初始化并渲染翻译结果GUI try SoGouTrans.init_transhtml_gui(1) ; msgBox ok } return ; } ;搜狗图片文字识别 class SoGouOcr { static url1:="https://fanyi.sogou.com/" ;搜狗主页 ,获取带有SNUID相关cookie static url2:="https://fanyi.sogou.com/picture" ;搜狗图片识别网页 ,获取uuid static url3:="https://pb.sogou.com/cl.gif?" ;打开文件会发送一个get请求 ,获取带有SNUID相关cookie static url4:="https://fanyi.sogou.com/api/transpc/picture/upload" ;上传文件接口,需要携带 snuid和FQV相关cookie static boundary = "----WebKitFormBoundaryaEHpMn3lywBtjPfE" ;formData边界 static User_Agent:="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" static cookie:="" ;访问url产生的cookie static uuid:="" ;方位url2返回的请求头中的uuid static snipaste_title:="Snipper - Snipaste" static html_header:="" ;<body>之前 static html_footer:="" ;</body>之后 static html_zoom:=0.7 ;网页缩放 static font_size:=25 ;设置字体大小 static html_top_color:="00ff00" ;顶部边框颜色 static pic_min_w:=40 ;图片最小允许宽度,小于这高度就会按照pic_scale放大 static pic_min_h:=30 ;图片最小允许高度,小于这高度就会按照pic_scale放大 static pic_scale:=2.5 ; 图片缩放比例 static result_source:="" ;返回的ocr识别 static result_trans:="" ;返回的ocr识别的翻译 static gui_w:=500 ;没有缩放之前的gui大小 ;请求所有的url, request_sogou_url(filepath) { req := ComObjCreate("WinHttp.WinHttpRequest.5.1") if(!SoGouOcr.cookie) SoGouOcr.get_url123(req) ObjRelease(req) ;释放内存 return SoGouOcr.get_url4(req,filepath) } ;打开搜狗翻译主页主要为了获取cookie中的snuid(url3,url4中),FQV(url中) ,wuid(url3中)使用 get_url123(req) { ;①请求url1 ; req.SetProxy(2, "127.0.0.1:8888") ;设置fiddler抓包服务器 req.Open("get", SoGouOcr.url1) req.setRequestHeader("User-Agent",SoGouOcr.User_Agent) ;在open之后 req.send() ; req.WaitForResponse() ; fileAppend ,% req.GetAllResponseHeaders(),C:\Users\Administrator\Desktop\cookie.txt headers:=req.GetAllResponseHeaders() ;获取所有相应头 ; msgBox % headers "`r`n" Util.getCookieMap(headers) SoGouOcr.cookie:=Util.getCookieMap(headers) ;②请求url2 req.Open("get", SoGouOcr.url2) req.send() ; req.WaitForResponse() SoGouOcr.uuid:=req.GetResponseHeader("UUID:") ;获取相关cookie ;③请求url3 Random, rand, 0.0, 1.0 _t:=Util.getTimeStamp() _r:=floor(1000* rand) uuid:=SoGouOcr.uuid snuid:=SoGouOcr.cookie.snuid.value wuid:=SoGouOcr.cookie.wuid.value url3:=SoGouOcr.url3 url3_with_param = %url3%uigs_productid=vs_web&vstype=translate&snuid=%snuid%&pagetype=index&type=imgtrans&uuid=%uuid%&fr=default&terminal=web&onerror=true&wuid=%wuid%&overIe10=1&abtest=3&uigs_cl=upload_click_home&_t=%_t%&_r=%_r%&uigs_st=0 ; msgBox % url3_with_param req.Open("get", url3_with_param) req.send() ; req.WaitForResponse() } ;④.上传文件 get_url4(req,filepath) { current_cookie:=% "ABTEST="SoGouOcr.cookie.ABTEST.value ";IPLOC="SoGouOcr.cookie.IPLOC.value ";SNUID=" SoGouOcr.cookie.SNUID.value ";FQV=" SoGouOcr.cookie.FQV.value ; current_cookie=ABTEST=6|1673104302|v17; IPLOC=CN5101;SNUID=DD5759A6D2D620B6A66D186BD31414E5;FQV=c7862eb243f3dfbcf3b287dcc047f0e7 ; src:="C:\Users\Administrator\Desktop\xx1.png" ; src:="C:\Users\Administrator\Desktop\xx2.png" while(!FileExist(filepath)) ;延迟3s中就退出 { sleep ,50 if(A_index>=60) return } src:=filepath extra_data={"from":"auto","to":"zh-CHS","imageName":"xx.png"} sec_ch_ua= "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109" objParam := {"fileData":[src],"fuuid":SoGouOcr.uuid,"extraData":extra_data} ;post数据 Util.getPostFormBinData(PostData, hdr_ContentType, objParam) req.Open("POST", SoGouOcr.url4,true) req.SetRequestHeader("Content-Type", hdr_ContentType) req.SetRequestHeader("sec-ch-ua",sec_ch_ua) req.SetRequestHeader("sec-ch-ua-mobile","?0") req.SetRequestHeader("sec-ch-ua-platform","Windows") req.SetRequestHeader("sec-Fetch-Dest","empty") req.SetRequestHeader("sec-Fetch-Mode","cors") req.SetRequestHeader("sec-Fetch-Site","same-origin") req.SetRequestHeader("User-Agent",SoGouOcr.User_Agent) req.SetRequestHeader("cookie",current_cookie) req.Send(PostData) req.WaitForResponse() result:=req.ResponseText ; fileAppend ,% result ,C:\Users\Administrator\Desktop\xx2.json return result } ;获取关键数据,成功返回map {contents:"xxx" content_list:[xx1,xx2,xx3]} 主要针对多行数据 get_ocr_resultMap(filepath) { result:=SoGouOcr.request_sogou_url(filepath) ; msgBox % result ; fileAppend % result,ocr.json rJson:=Util.parseJsonStr(result) retMap :={} if(rJson.status=0) { data:=rJson.data try{ data["result"] }catch e{ return } content_results:=data["result"] ; msgBox % content_results[0] contents:="" trans:="" content_list:=[] trans_list:=[] for content in content_results { contents:=contents content.content "`r`n" content_list[A_index]:=content.content trans:=trans content.trans_content "`r`n" trans_list[A_index]:=content.trans_content } retMap["contents"]:=RTrim(contents,"`r`n") retMap["content_list"]:=content_list retMap["trans"]:=RTrim(trans,"`r`n") retMap["trans_list"]:=trans_list ; msgBox % retMap.contents return retMap } } ;显示翻译结果 show(flag="raw") { ; msgBox show clipboard_save:=ClipboardAll SetKeyDelay, 0 ControlSend, , ^c, % SoGouOcr.snipaste_title ;保存图片 clipWait,3,1 ;第一个参数是等待的s数,第二个参数1是等待任意类型,0(缺省) 等待文本文件等、 MouseGetPos ,xpos,ypos SoGouTrans.create_ico_gui(xpos,ypos) WinSet, AlwaysOnTop, ON, % SoGouTrans.ico_title SoGouTrans.html_ready:=0 ;加载完成变成1 SoGouTrans.ico_new:=0 ;创建新的ico变成1 SoGouTrans.animate_timer_count:=0 ;执行一次加一次 SetTimer,label_ico_click_animate,% SoGouTrans.animate_timer_gap ;timer后面必须是一个timer不然无法异步执行 filepath:=SoGouOcr.save_pic() ; msgBox % filepath ; fileAppend ,% "`n" filepath ,%A_Temp%\ahkFYtmp.txt if(!filepath) ;处理图片 { clipboard:=clipboard_save ;恢复粘贴板 return } clipboard:=clipboard_save ;恢复粘贴板 ; fileappend ,% filepath ,%A_Temp%\ahkFYtmp.txt SoGouOcr.result_trans:="" SoGouOcr.result_source:="" content_map:=SoGouOcr.get_ocr_resultMap(filepath) ;图片渲染的数据 SoGouOcr.result_source:=content_map.contents SoGouOcr.result_trans:=content_map.trans ; 翻译结果 if(content_map) SoGouOcr.create_ocr_html(content_map,flag) ;创建gui IfExist ,% filepath ;清除tmp中图片 FileDelete, % filepath SoGouTrans.html_ready:=1 Gui ,translate:destroy } ;创建html的GUI create_ocr_html(content_map,flag) { MouseGetPos ,xpos,ypos xpos:=xpos-20 ypos:=ypos-20 result_html:=SoGouOcr.get_result_guihtml(content_map,flag) global WB2 ;浏览器对象 global MenuHwnd2 ;句柄 global srcCopyButton1 ;复制按钮1 global srcCopyButton2 ;复制按钮2 global exitBtn1 ;退出按钮 ; global convertBtn1 ;切换按钮 gui_width:=SoGouOcr.gui_w * SoGouOcr.html_zoom html_title:="ahk_sogouocr_result_v1" Gui ,sgocr:New,,% html_title ; sleep ,100 Gui ,sgocr:Add, ActiveX,x0 y0 w%gui_width% h1080 vWB2, Shell.Explorer ; 最后一个参数是ActiveX组件的名称。 Gui, sgocr:Color, 30f0ca ; Gui ,sgocr: -Caption +ToolWindow +HwndocrHwnd Gui ,sgocr:+LastFound +AlwaysOnTop -Caption +ToolWindow +HwndocrHwnd WB2.silent := true ;Surpress JS Error boxes Util.Display(WB2,result_html) while WB2.readystate != 4 or WB2.busy sleep 10 div_h:=WB2.document.getElementById("mainDiv").offsetHeight ;当前div高度 srcCopyButton1 := WB2.document.getElementById("src-clipboard") ;复制按钮 srcCopyButton2 := WB2.document.getElementById("target-clipboard") ;复制按钮 exitBtn1 := WB2.document.getElementById("exitBtn1") ;退出 ComObjConnect(srcCopyButton1, "srcCopyButton1_") ComObjConnect(srcCopyButton2, "srcCopyButton2_") ComObjConnect(exitBtn1, "exitBtn1_") ; ComObjConnect(convertBtn1, "convertBtn1_") div_h:=div_h*SoGouOcr.html_zoom ;边界检测 xpos:= xpos+gui_width>A_ScreenWidth?A_ScreenWidth-gui_width:xpos ypos:= ypos+div_h>A_ScreenHeight-40?A_ScreenHeight-div_h-40:ypos Gui sgocr:Show ,x%xpos% y%ypos% w%gui_width% h%div_h% Util.FrameShadow(ocrHwnd) ; msgBox ok } ;获取渲染html ,清空body然后放入构造的html get_result_guihtml(content_map,flag) { url2:="https://fanyi.sogou.com/picture" ;搜狗图片识别网页 ,获取uuid WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1") ;创建http对象 WebRequest.Open("GET",url2) ;必须有http:// WebRequest.Send() result := WebRequest.ResponseText ObjRelease(WebRequest) ;释放内存 start_element=<!--[if lte IE 9]> <script> ;搜索结果只有一个 end_element=</div><script> ; 从开始元素开始搜索 StringReplace, result, result, "//,"https:// ,All ;把路径转换为绝对路径 if(!SoGouOcr.head_html||!SoGouOcr.footer_html) { start_pos:=instr(result,start_element,1,1) ;找到body开头 if(!start_pos) return ; end_pos:= instr(result,end_element,1,start_pos) ;参数依次是1.目标字符,2.要匹配的字符,3.是否大小写消息敏感,4.起始位置 if(!end_pos) return SoGouOcr.head_html:=subStr(result,1,start_pos-1) SoGouOcr.footer_html:=subStr(result,end_pos+strLen("</div>")) ;substr不指定长度就是直接取到最后一个字符 } zoom:=SoGouOcr.html_zoom font_size:=SoGouOcr.font_size if(strLen(SoGouOcr.result_source)>15) font_size:=font_size*0.9 if(strLen(SoGouOcr.result_source)>30) font_size:=font_size*0.9 if(strLen(SoGouOcr.result_source)>40) font_size:=font_size*0.9 border_color:=SoGouOcr.html_top_color span_elements:="" ;识别后的文 span_elements2:="" ;翻译后 exit_left:=zoom*SoGouOcr.gui_w-22,exit_top:=10,exit_zoom:=0.4 convert_left:=10, convert_bottom:=30,convert_zoom:=0.5 gui_w:=SoGouOcr.gui_w * SoGouOcr.html_zoom gui_r:=gui_w+10 ;右滑块宽度10px to_right=window.scrollTo(%gui_r%,0) to_left=window.scrollTo(0,%gui_w%) ; main_div=<div id="mainDiv" style="zoom:%html_scala%;border-top:5px solid #%border_color%;width:%div_w%"> main_div=<div id="mainDiv" style="zoom:%zoom%;border-top:5px solid #%border_color%"> div_element_start=%main_div%<div class="trans-box pic"><div class="pic-result-box"><div class="pic-from"><div class="text-box" style="font-size:%font_size%px" ><p> html_style=<style>.source_trans_convert{width: 30px;height: 30px;z-index:100;zoom:%convert_zoom%;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -372px -176px;}.source_trans_convert:hover{ cursor:pointer;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat; background-position: -372px -142px;zoom:0.51}.html_gui_exit{width: 38px;height: 38px;zoom:%exit_zoom%;position: fixed;left:%exit_left%px;top: %exit_top%px;z-index:2;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -78px -319px;}.html_gui_exit:hover{cursor:pointer;background: url("https://search.sogoucdn.com/translate/pc/static/img/sprite_common_translate.ed1fb14.png") no-repeat;background-position: -38px -319px;zoom:0.402}</style> html_script=<script>var flag=true;window.onload = function(){var div_h = document.getElementById('mainDiv').offsetHeight;document.getElementById('convertBtn1').style.position="fixed";document.getElementById('convertBtn1').style.top= (div_h*%zoom%-%convert_bottom%)+"px";document.getElementById('convertBtn1').style.left="%convert_left%px";};function convert_click(){if(flag){%to_right%}else{%to_left%}flag=!flag}</script> ico_div=<div class="html_gui_exit" id="exitBtn1"></div><div class="source_trans_convert" onclick="convert_click()" id="convertBtn1"></div> ;渲染的数据是<span id="left-17-s">SMS202301170423250412</span> arr1:= content_map.content_list arr2:=content_map.trans_list ; msgBox % "content:" content_map.contents for k,v in arr1 span_elements=%span_elements% <span id="left-%A_index%-s">%v%</span> for m,n in arr2 span_elements2=%span_elements2% <span id="right-%A_index%-s">%n%</span> ; msgBox % "html_frag:" span_elements ; msgBox % "html_frag:" span_elements2 copy:=Util.decodeUtf8("\u590D\u5236") ;复制 right_copy=<div id="target-clipboard" class="btn-copy" >%copy%</div> right_element=<div class="pic-to"><div class="text-box"><p>%span_elements2%</p></div>%right_copy%</div> div_element_end=</p></div><div id="src-clipboard" class="btn-copy">%copy%</div></div>%right_element%</div></div> result_html:=SoGouOcr.head_html html_style html_script ico_div div_element_start span_elements div_element_end SoGouOcr.footer_html fileAppend , % result_html ,C:\Users\Administrator\Desktop\result.html ,utf-8 return result_html } ;保存图片到临时文件夹并返回文件 save_pic(filename:="") { imageUtil.gdiplusStartup() ;开始GDI pBitmap:=imageUtil.from_clipboard() ; msgBox % "pBitmap:" pBitmap if(pBitmap<0) ;获取粘贴板数据 return ; DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0) ;获取图片宽度 DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0) ;获取图片高度 ; msgBox % "width:height=" width ":" height if(width<SoGouOcr.pic_min_w || height<SoGouOcr.pic_min_h ) { imageUtil.BitmapScale(pBitmap,SoGouOcr.pic_scale) ;缩放 } ; msgBox % "pBitmap3:" pBitmap if(!filename) { filename:=A_temp "\ahk_ocr_" A_YYYY "-" A_MM "-" A_DD "-" A_Hour "-" A_Min "-" A_Sec ".png" filename:=imageUtil.put_file(pBitmap,filename) } imageUtil.gdiplusShutdown() ;关闭GDI return filename } } ; ==================== Util ==================== Class Util { ;字符串转换为二进制 BinArr_FromString(str) { oADO := ComObjCreate("ADODB.Stream") oADO.Type := 2 ; adTypeText oADO.Mode := 3 ; adModeReadWrite oADO.Open oADO.Charset := "UTF-8" oADO.WriteText(str) oADO.Position := 0 oADO.Type := 1 ; adTypeBinary oADO.Position := 3 ; Skip UTF-8 BOM return oADO.Read, oADO.Close } ;把文件转换为二进制 BinArr_FromFile(FileName) { oADO := ComObjCreate("ADODB.Stream") oADO.Type := 1 ; adTypeBinary oADO.Open oADO.LoadFromFile(FileName) return oADO.Read, oADO.Close } ;合并二进制数据 BinArr_Join(Arrays*) { oADO := ComObjCreate("ADODB.Stream") oADO.Type := 1 ; adTypeBinary oADO.Mode := 3 ; adModeReadWrite oADO.Open For i, arr in Arrays oADO.Write(arr) oADO.Position := 0 return oADO.Read, oADO.Close } ;把二进制转换为字符串 BinArr_ToString(BinArr, Encoding := "UTF-8") { oADO := ComObjCreate("ADODB.Stream") oADO.Type := 1 ; adTypeBinary oADO.Mode := 3 ; adModeReadWrite oADO.Open oADO.Write(BinArr) oADO.Position := 0 oADO.Type := 2 ; adTypeText oADO.Charset := Encoding return oADO.ReadText, oADO.Close } ;把二进制转换为文件 BinArr_ToFile(BinArr, FileName) { oADO := ComObjCreate("ADODB.Stream") oADO.Type := 1 ; adTypeBinary oADO.Open oADO.Write(BinArr) oADO.SaveToFile(FileName, 2) oADO.Close } ;产生随机边界 RandomBoundary() { str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z" Sort, str, D| Random str := StrReplace(str, "|") Return SubStr(str, 1, 12) } ;判断当前类型 MimeType(FileName) { n := FileOpen(FileName, "r").ReadUInt() Return (n = 0x474E5089) ? "image/png" : (n = 0x38464947) ? "image/gif" : (n&0xFFFF = 0x4D42 ) ? "image/bmp" : (n&0xFFFF = 0xD8FF ) ? "image/jpeg" : (n&0xFFFF = 0x4949 ) ? "image/tiff" : (n&0xFFFF = 0x4D4D ) ? "image/tiff" : "application/octet-stream" } ;解析json字符串、 parseJsonStr(jsonStr) { if(inStr(jsonStr,"`\")) ;包含转义字符 { tmp_str=EABD9414432D47859B9BCD78ACA11848 StringReplace, jsonStr, jsonStr, `\ ,% tmp_str,replaceAll StringReplace, jsonStr, jsonStr, `" ,`\`",replaceAll StringReplace, jsonStr, jsonStr, % tmp_str ,`\`\,replaceAll }else{ StringReplace, jsonStr, jsonStr, `" ,`\`",replaceAll } oSC := ComObjCreate("ScriptControl") oSC.Language := "JScript" Script = var Encoded = eval("(%jsonStr%)") oSC.ExecuteStatement(Script) result:=oSC.Eval("Encoded") ObjRelease(oSC) Return result } ;返回一个cookie 的map={a:{value:"hello",path:"/" , expires:"Sun, 22 Jan 2023 03:46:59 GMT"},cookie:"xxx",size:n} ;req.GetAllResponseHeaders() 获取所有cookie getCookieMap(headers) { cookie_map:={} cookie_str:="" ; msgBox % headers size:=0 Loop, parse, headers, `n, `r ; 在 `r 之前指定 `n, 这样可以同时支持对 Windows 和 Unix 文件的解析. { head_line:=trim(A_loopField) ; msgBox % head_line tmp_map:={} key:="" if(inStr(head_line ,"Set-Cookie:")=1) { size:=size+1 cookie_str:=cookie_str head_line "`r`n" StringReplace ,cookie_value,% head_line,Set-Cookie: key_value_arr:=StrSplit(cookie_value ,"`;") Loop ,% key_value_arr.MaxIndex() { part_cookie:=key_value_arr[A_index] tmp_arr:=StrSplit(trim(part_cookie) ,"=") if(tmp_arr.MaxIndex()>1) { if(A_index=1) { key := trim(tmp_arr[1]) tmp_map["value"]:=trim(tmp_arr[2]) }else{ tmp_map[trim(tmp_arr[1])]:=trim(tmp_arr[2]) } } } cookie_map[key]:=tmp_map } } cookie_map["cookie"]:=cookie_str cookie_map["size"]:=size return cookie_map } ;获取post请求中payload ,在post慢中对应数据类型为body中的form-data ,数据类型为二进制 ;retData 返回的二进制数据,retHeader请求头,objParam入参对象 [] 中会被认为是文件 ;示例:objParam := {"fileData":[src],"fuuid":"aa409350-e00c-49df-9f20-517796457e68","extraData":extra_data} ; getPostFormBinData(ByRef retData, ByRef retHeader, objParam) { CRLF := "`r`n" ; Create a random Boundary BoundaryLine := "------------------------------" . Boundary ; Loop input paramters binArrs := [] For k, v in objParam { If IsObject(v) { For i, FileName in v { ;当前二进制数据来源于文件 str := BoundaryLine . CRLF . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF . "Content-Type: " . Util.MimeType(FileName) . CRLF . CRLF binArrs.Push( Util.BinArr_FromString(str) ) binArrs.Push( Util.BinArr_FromFile(FileName) ) binArrs.Push( Util.BinArr_FromString(CRLF) ) } } Else { str := BoundaryLine . CRLF . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF . v . CRLF binArrs.Push( Util.BinArr_FromString(str) ) } } str := BoundaryLine . "--" . CRLF binArrs.Push( Util.BinArr_FromString(str) ) ; Finish retData := Util.BinArr_Join(binArrs*) retHeader := "multipart/form-data; boundary=----------------------------" . Boundary } ;获取当前时间戳 getTimeStamp() { startTime := "19700101000000" nowTime := A_NowUTC EnvSub, nowTime, startTime, seconds return % nowTime*1000+A_MSec } ;对utf-8解码 decodeUtf8(value) { i := 0 while (i := InStr(value, "\",, i+1)) { if !(SubStr(value, i+1, 1) == "u") this.ParseError("\", text, pos - StrLen(SubStr(value, i+1))) uffff := Abs("0x" . SubStr(value, i+2, 4)) if (A_IsUnicode || uffff < 0x100) value := SubStr(value, 1, i-1) . Chr(uffff) . SubStr(value, i+6) } Return,value } ;窗口加上阴影 FrameShadow(HGui) { DllCall("dwmapi\DwmIsCompositionEnabled","IntP",_ISENABLED) ; Get if DWM Manager is Enabled if !_ISENABLED ; if DWM is not enabled, Make Basic Shadow DllCall("SetClassLong","UInt",HGui,"Int",-26,"Int",DllCall("GetClassLong","UInt",HGui,"Int",-26)|0x20000) else VarSetCapacity(_MARGINS,16) , NumPut(0,&_MARGINS,0,"UInt") , NumPut(0,&_MARGINS,4,"UInt") , NumPut(1,&_MARGINS,8,"UInt") , NumPut(0,&_MARGINS,12,"UInt") , DllCall("dwmapi\DwmSetWindowAttribute", "Ptr", HGui, "UInt", 2, "Int*", 2, "UInt", 4) , DllCall("dwmapi\DwmExtendFrameIntoClientArea", "Ptr", HGui, "Ptr", &_MARGINS) } ;展示html Display(WB,html_str) { Count:=0 while % FileExist(f:=A_Temp "\" A_TickCount A_NowUTC "-tmp" Count ".DELETEME.html") Count+=1 FileAppend,%html_str%,%f%,UTF-8 ; f=C:\Users\Administrator\Desktop\xx4.html WB.Navigate("file://" . f) ; msgBox % WB.readystate } ;获取粘贴板中png的流 get_clipboard_pngstream() { ; Open the clipboard with exponential backoff. loop if DllCall("OpenClipboard", "ptr", A_ScriptHwnd) break else if A_Index < 6 Sleep (2**(A_Index-1) * 30) else throw Exception("Clipboard could not be opened.") png := DllCall("RegisterClipboardFormat", "str", "png", "uint") if !DllCall("IsClipboardFormatAvailable", "uint", png) pStream:=1 if !(hData := DllCall("GetClipboardData", "uint", png, "ptr")) pStream:=2 ; Allow the stream to be freed while leaving the hData intact. ; Please read: https://devblogs.microsoft.com/oldnewthing/20210930-00/?p=105745 DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", false, "ptr*", pStream:=0, "uint") DllCall("CloseClipboard") return pStream } ToBase(n,b) { return (n < b ? "" : Util.ToBase(n//b,b)) . ((d:=Mod(n,b)) < 10 ? d : Chr(d+55)) } } ; ==================== 图像操作类 ==================== class imageUtil{ ;获取粘贴板数据返回bitmap的指针 pBitmap ,非图片时报错 from_clipboard() { ; Open the clipboard with exponential backoff. loop if DllCall("OpenClipboard", "ptr", A_ScriptHwnd) break else if A_Index < 6 Sleep (2**(A_Index-1) * 30) else throw Exception("Clipboard could not be opened.") ; Fallback to CF_BITMAP. This format does not support transparency even with put_hBitmap(). if !DllCall("IsClipboardFormatAvailable", "uint", 2) { DllCall("CloseClipboard") return -1 } if !(hbm := DllCall("GetClipboardData", "uint", 2, "ptr")) { DllCall("CloseClipboard") return -2 } DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", hbm, "ptr", 0, "ptr*", pBitmap:=0) DllCall("DeleteObject", "ptr", hbm) DllCall("CloseClipboard") return pBitmap } ;缩放图片传入bitmap的指针,scale缩放 [200,200] 或者1.5倍 BitmapScale(ByRef pBitmap, scale) { if not (IsObject(scale) && ((scale[1] ~= "^\d+$") || (scale[2] ~= "^\d+$")) || (scale ~= "^\d+(\.\d+)?$")) throw Exception("Invalid scale.") ; Get Bitmap width, height, and format. DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0) DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0) DllCall("gdiplus\GdipGetImagePixelFormat", "ptr", pBitmap, "int*", format:=0) if IsObject(scale) { safe_w := (scale[1] ~= "^\d+$") ? scale[1] : Round(width / height * scale[2]) safe_h := (scale[2] ~= "^\d+$") ? scale[2] : Round(height / width * scale[1]) } else { safe_w := Ceil(width * scale) safe_h := Ceil(height * scale) } ; Avoid drawing if no changes detected. if (safe_w = width && safe_h = height) return pBitmap ; Create a new bitmap and get the graphics context. DllCall("gdiplus\GdipCreateBitmapFromScan0" , "int", safe_w, "int", safe_h, "int", 0, "int", format, "ptr", 0, "ptr*", pBitmapScale:=0) DllCall("gdiplus\GdipGetImageGraphicsContext", "ptr", pBitmapScale, "ptr*", pGraphics:=0) ; Set settings in graphics context. DllCall("gdiplus\GdipSetPixelOffsetMode", "ptr", pGraphics, "int", 2) ; Half pixel offset. DllCall("gdiplus\GdipSetCompositingMode", "ptr", pGraphics, "int", 1) ; Overwrite/SourceCopy. DllCall("gdiplus\GdipSetInterpolationMode", "ptr", pGraphics, "int", 7) ; HighQualityBicubic ; Draw Image. DllCall("gdiplus\GdipCreateImageAttributes", "ptr*", ImageAttr:=0) DllCall("gdiplus\GdipSetImageAttributesWrapMode", "ptr", ImageAttr, "int", 3) ; WrapModeTileFlipXY DllCall("gdiplus\GdipDrawImageRectRectI" , "ptr", pGraphics , "ptr", pBitmap , "int", 0, "int", 0, "int", safe_w, "int", safe_h ; destination rectangle , "int", 0, "int", 0, "int", width, "int", height ; source rectangle , "int", 2 , "ptr", ImageAttr , "ptr", 0 , "ptr", 0) DllCall("gdiplus\GdipDisposeImageAttributes", "ptr", ImageAttr) ; Clean up the graphics context. DllCall("gdiplus\GdipDeleteGraphics", "ptr", pGraphics) DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap) return pBitmap := pBitmapScale } ;把bitmap输出为文件的 bitmap指针(pBitmap) ,filepath:目标位置 比如 c:\xx.png,返回文件位置 put_file(pBitmap, filepath := "", quality := "") { ; Thanks tic - https://www.autohotkey.com/boards/viewtopic.php?t=6517 extension := "png" imageUtil.select_codec(pBitmap, extension, quality, pCodec, ep, ci, v) ; Write the file to disk using the specified encoder and encoding parameters with exponential backoff. loop if !DllCall("gdiplus\GdipSaveImageToFile", "ptr", pBitmap, "wstr", filepath, "ptr", pCodec, "ptr", (ep) ? &ep : 0) break else if A_Index < 6 Sleep (2**(A_Index-1) * 30) else throw Exception("Could not save file to disk.") return filepath } select_codec(pBitmap, extension, quality, ByRef pCodec, ByRef ep, ByRef ci, ByRef v) { ; Fill a buffer with the available image codec info. DllCall("gdiplus\GdipGetImageEncodersSize", "uint*", count:=0, "uint*", size:=0) DllCall("gdiplus\GdipGetImageEncoders", "uint", count, "uint", size, "ptr", &ci := VarSetCapacity(ci, size)) ; struct ImageCodecInfo - http://www.jose.it-berater.org/gdiplus/reference/structures/imagecodecinfo.htm loop { if (A_Index > count) throw Exception("Could not find a matching encoder for the specified file format.") idx := (48+7*A_PtrSize)*(A_Index-1) } until InStr(StrGet(NumGet(ci, idx+32+3*A_PtrSize, "ptr"), "UTF-16"), extension) ; FilenameExtension ; Get the pointer to the clsid of the matching encoder. pCodec := &ci + idx ; ClassID ; JPEG default quality is 75. Otherwise set a quality value from [0-100]. if (quality ~= "^-?\d+$") and ("image/jpeg" = StrGet(NumGet(ci, idx+32+4*A_PtrSize, "ptr"), "UTF-16")) { ; MimeType ; Use a separate buffer to store the quality as ValueTypeLong (4). VarSetCapacity(v, 4), NumPut(quality, v, "uint") ; struct EncoderParameter - http://www.jose.it-berater.org/gdiplus/reference/structures/encoderparameter.htm ; enum ValueType - https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.encoderparametervaluetype ; clsid Image Encoder Constants - http://www.jose.it-berater.org/gdiplus/reference/constants/gdipimageencoderconstants.htm VarSetCapacity(ep, 24+2*A_PtrSize) ; sizeof(EncoderParameter) = ptr + n*(28, 32) NumPut( 1, ep, 0, "uptr") ; Count DllCall("ole32\CLSIDFromString", "wstr", "{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}", "ptr", &ep+A_PtrSize, "uint") NumPut( 1, ep, 16+A_PtrSize, "uint") ; Number of Values NumPut( 4, ep, 20+A_PtrSize, "uint") ; Type NumPut( &v, ep, 24+A_PtrSize, "ptr") ; Value } } ;开始GDI gdiplusStartup() { DllCall("LoadLibrary", "str", "gdiplus") VarSetCapacity(si, A_PtrSize = 4 ? 16:24, 0) ; sizeof(GdiplusStartupInput) = 16, 24 NumPut(0x1, si, "uint") DllCall("gdiplus\GdiplusStartup", "ptr*", pToken:=0, "ptr", &si, "ptr", 0) } ;关闭GDI释放内存 gdiplusShutdown() { DllCall("gdiplus\GdiplusShutdown", "ptr", pToken) DllCall("FreeLibrary", "ptr", DllCall("GetModuleHandle", "str", "gdiplus", "ptr")) } } ; ==================== 搜狗翻译 ==================== class SoGouTrans { ; static ico_shape :="H" ; static ico_shape :="\u2742" ;❂ ; static ico_shape :="\u2664" ;♤ ; static ico_shape :="\u2B1B" ;⬛ ; static ico_shape :="\u2693" ;⚓ ; static ico_shape :="\u2693" ;⚓ ; static ico_shape :="\uD83D\uDC10" ;🐐 ; static ico_shape :="\uD83C\uDF51" ;🍑 ; static ico_shape :="\uD83C\uDFEF" ;🏯 ; static ico_shape :="\u2764" ;❤ ; static ico_shape :="\u265E" ;♞ ; static ico_shape :="\u2663" ;♣ static ico_shape :="\u265C" ;♜ ; static ico_shape :="\u2602" ;☂ ; static ico_shape :="\u2660" ;♠ ;图形字符对应的unicode编码 ; static ico_wait_shape :="\u25CF" ;● 等待的图标 static ico_wait_shape :="." ;● 等待的图标 static ico_size:= 15 ;图标大小, 对象变量必须在new中初始化 static ico_offset_x:= 10 ;翻译图标偏离鼠标点击位置x轴的距离 static ico_offset_y:= 6 ;翻译图标偏离鼠标点击位置y轴的距离 static ico_color:= "00ff00" ;翻译图标颜色,只支持16进制ffffff,不支持颜色单词 static ico_title:="ahk_translate_ico_v1" ;翻译的小图标 static html_title:="ahk_translate_result_v1" ;翻译的小图标 static html_scala:=0.8 ;缩放 范围(0-1] (0最小,1最大) static ico_hwnd ;小图标的句柄 static html_hwnd ;html的句柄 static select_word ;选中字符 static ico_x ;图标所在x位置 static ico_y ;图标所在y位置 static hide_flag ;当前图标在单击其它区域后后影藏,除非是 static trans_doubclick_on:=0 ;是否支持双击选词 0不支持,1支持 static cursor_drag_gaptime:=300 ;判断鼠标按下到弹起时间差单位ms 来判断拖动 static cursor_dbclick_gaptime:=100 ;判断鼠标双击时间差单位ms 来判断双击 static html_head ;缓存头部 static html_foot ;缓存尾部 static gui_width:=520 ;显示html的宽度,缩放之前 static ignore_title ;忽略翻译的 static grid_width:=15 ;背景取样格子大小,单位像素 static LB_down_cursor ; 左键按下时鼠标形状 static timer_flag ; 判断当前是否还在定时器中执行 static ico_destroy_time :=3000 ;ico在多少ms内不点击的情况下消失 static ico_timer_count:=0 ;消失ico的timer执行的次数 static ico_new ;是否新建ico ,1是,0否 static html_ready :=0 ;当前html页面是否加载完成 static animate_timer_count:=0 ;当前动画计时器执行次数 static animate_timer_gap:=200 ;定时器周期时长 单位ms static animate_out_time :=10000 ;动画超时时间 (超过时间关闭定时器),单位ms static click_down_posx ; static cursor_row_min_dist :=6 ;横向距离 单位px static use_shortcut_trans :=0 ;是否使用快捷键翻译 ,1使用快捷键翻译,0使用图标翻译。 static point_to_ico := 5 ;点到ico的距离会减去该值 ;在鼠标滑动位置创建一个图标 create_ico_gui(obs_x,obs_y) { SoGouTrans.ico_x :=obs_x+SoGouTrans.ico_offset_x+3 SoGouTrans.ico_y :=obs_y+SoGouTrans.ico_offset_y+13 obs_x:=obs_x+SoGouTrans.ico_offset_x obs_y:=obs_y+SoGouTrans.ico_offset_y ico_shape :=Util.decodeUtf8(SoGouTrans.ico_shape) global Ico_text bg_color :=SoGouTrans.calculate_bg_color(obs_x,obs_y,SoGouTrans.ico_size) ;取点分析背景颜色 ; msgBox % bg_color ; bg_color :=2b2b2b ico_size:=SoGouTrans.ico_size ico_color:=SoGouTrans.ico_color gui_w:=ico_size>16?(2*ico_size-16)+8:ico_size+8 gui_h:=gui_w gui_title:=SoGouTrans.ico_title wait3_offset_x:=2.5*ico_size gui_w:=wait3_offset_x+10 Gui ,translate:New, ,% gui_title Gui, translate:Color, %bg_color% ; Gui, translate:Color, ffffff Gui ,translate:+LastFound +HwndMenuHwnd +AlwaysOnTop -Caption +ToolWindow SoGouTrans.ico_hwnd:=MenuHwnd Gui, translate:Font, c%ico_color% q1 thin s%ico_size% , Verdana Gui ,translate:Add, Text, vIco_text x0 y0 c%ico_color% glabel_gui_event, % ico_shape ; label_gui_event标签处理gui事件 WinSet, TransColor, %bg_color% 250 ;设置颜色透明 Gui ,translate:Show, x%obs_x% y%obs_y% w%gui_w% h%gui_h% NoActivate ;NoActivate 让当前活动窗口继续保持活动状态. WinSet, AlwaysOnTop, Off, % gui_title ;去掉总在最上面限制,在切换窗口的时候可以隐藏,但是并不会关闭 GuiControl,hide ,Ico_wait_text1 GuiControl,hide ,Ico_wait_text2 GuiControl,hide ,Ico_wait_text3 select_str:=Util.getSelectStr() ;获取光标选中字符 StringReplace, select_str,select_str, `r,, All StringReplace, select_str,select_str, `n,, All StringReplace, select_str,select_str, `t,, All SoGouTrans.select_word:= select_str setTimer ,label_destroy_ico,off ;去掉之前的定时器 setTimer ,label_destroy_ico ,50 ;延时后去掉ico SoGouTrans.ico_timer_count:=0 SoGOuTrans.ico_new:=1 ; fileappend ,% SoGouTrans.select_word,C:\Users\Administrator\Desktop\select.txt ; msgBox % strlen(trim(SoGouTrans.select_word,OmitChars = "`r`n")) ; msgBox % strLen(select_str) return 1 } ;计算背景颜色,采集20个点,判断颜色最多的,去掉背景会残留只有融入背景才能让图形更圆滑 calculate_bg_color(obs_x,obs_y,font_size) { grid_width:=SoGouTrans.grid_width ;取样格子宽度 x:=obs_x+font_size+10 y:=obs_y+font_size+10 color_map:={} loop , 5 { i:=A_Index loop 4 { j:=A_Index PixelGetColor,current_color, % x+j*grid_width,% obs_y+i*grid_width ,RGB ; msgBox % x+j*grid_width "," y+i*grid_width " color:" current_color if(!color_map[current_color]) color_map[current_color]:=1 else color_map[current_color]:=color_map[current_color]+1 } } tmp_count:=0 tmp_color:=0 for key,value in color_map { if(color_map[key]>tmp_count) { tmp_count:=color_map[key] tmp_color:=key } } ; msgBox % msg "color:" Util.ToBase(tmp_color,16) return Util.ToBase(tmp_color,16) } }
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。
评论(0)