#NoEnv
SetBatchLines, -1
CoordMode, ToolTip
PrLibDownload(377452, 1)
MsgBox, Completed!
PrLibDownload(item, page, zoom := "max") {
if !GetItemInfo(item, page, zoom, width, height, file, id1, id2)
Return
if !GetChunkDimension(id1, id2, file, zoom, chunkWidth, chunkHeight)
Return
BuildImg(id1, id2, file, zoom, width, height, chunkWidth, chunkHeight, item . "-" . page . ".tif")
}
GetItemInfo(item, page, ByRef zoom, ByRef width, ByRef height, ByRef file, ByRef id1, ByRef id2) {
ToolTip, Getting item info ..., 2, 2, 1
Loop 1 {
if !html := GetHtml("https://www.prlib.ru/item/" . item) {
error := "Failed to load html"
break
}
if !RegExMatch(html, "i)public\\/([a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12})\\/(\d+)", id) {
error := "Failed to get IDs"
break
}
if !metadataJson := GetHtml("https://content.prlib.ru/metadata/public/" . id1 . "/" . id2 . "/" . id1 . ".json") {
error := "Failed to get metadata"
break
}
JS := GetJS()
try jsObj := JS.eval("(" . metadataJson . ")")
catch {
error := "Bad Json data"
break
}
maxPage := jsObj["pgs"].length
if (page > maxPage) {
MsgBox, There is no page %page%, max page number is %maxPage%
Return
}
try maxZoom := jsObj.max_zoom
catch {
error := "Unknown json format"
break
}
(zoom = "max" && zoom := maxZoom)
if (maxZoom < zoom) {
error := "Zoom " . zoom . " is not supported. Max zoom is " . maxZoom
break
}
try width := jsObj["pgs"][page - 1]["d"][zoom].w
catch {
error := "Filed to get image width: unknown json format"
break
}
try height := jsObj["pgs"][page - 1]["d"][zoom].h
catch {
error := "Filed to get image height: unknown json format"
break
}
try file := jsObj["pgs"][page - 1].f
catch {
error := "Filed to get image fileName: unknown json format"
break
}
}
ToolTip,,,, 1
if !error
Return true
else
MsgBox, % error
}
GetChunkDimension(id1, id2, file, zoom, ByRef chunkWidth, ByRef chunkHeight) {
static preUrl := "https://content.prlib.ru/fcgi-bin/iipsrv.fcgi?FIF=/var/data/scans/public/"
if !hBitmap := GetHBitmapFromImageURL(preUrl . id1 . "/" . id2 . "/" . file . "&JTL=" . zoom . ",0") {
MsgBox, Failed to get bitmap from url
Return
}
VarSetCapacity(BITMAP, sz := 4*4 + A_PtrSize*2, 0)
DllCall("GetObject", "Ptr", hBitmap, "Int", sz, "Ptr", &BITMAP)
DllCall("DeleteObject", "Ptr", hBitmap)
chunkWidth := NumGet(BITMAP, 4, "UInt")
chunkHeight := NumGet(BITMAP, 8, "UInt")
Return true
}
BuildImg(id1, id2, file, zoom, width, height, chunkWidth, chunkHeight, outputTif) {
static preUrl := "https://content.prlib.ru/fcgi-bin/iipsrv.fcgi?FIF=/var/data/scans/public/"
countWidth := Ceil(width/chunkWidth)
countHeight := Ceil(height/chunkHeight)
chunkCount := countWidth*countHeight
GdipInst := new GDIp
pBitmap := GdipInst.CreateBitmap(width, height)
G := GdipInst.GraphicsFromImage(pBitmap)
Loop % chunkCount {
ToolTip % A_Index . " of " . chunkCount . " is downloading ...", 2, 2, 1
if !pIUnknown := GetImgResponseStream(preUrl . id1 . "/" . id2 . "/" . file . "&JTL=" . zoom . "," . A_Index - 1) {
MsgBox, 4,, Failed to load a chunk. Continue?
IfMsgBox, No
break
else
continue
}
pBitmapChunk := GdipInst.BitmapFromIStream(pIUnknown)
DllCall("OleAut32\VariantClear", "PtrP", pIUnknown)
GdipInst.DrawImage(G, pBitmapChunk, chunkWidth*mod((A_Index - 1), countWidth)
, chunkHeight*(A_Index//(countWidth + 0.01))
, chunkWidth, chunkHeight, 0, 0, chunkWidth, chunkHeight)
GdipInst.DisposeImage(pBitmapChunk)
}
ToolTip,,,, 1
GdipInst.SaveBitmap(pBitmap, outputTif)
GdipInst.DeleteGraphics(G)
GdipInst.DisposeImage(pBitmap)
}
GetHtml(url) {
whr := ComObjCreate("Msxml2.XMLHTTP.6.0")
whr.Open("GET", url, false)
whr.SetRequestHeader("Pragma", "no-cache")
whr.SetRequestHeader("Cache-Control", "no-cache, no-store")
whr.Send()
Return html := whr.ResponseText
}
GetJS() {
static doc, JS
if !doc {
doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
}
Return JS
}
GetHBitmapFromImageURL(url) {
if !pIUnknown := GetImgResponseStream(url)
Return
GdipInst := new GDIp
pBitmap := GdipInst.BitmapFromIStream(pIUnknown)
DllCall("OleAut32\VariantClear", "PtrP", pIUnknown)
hBitmap := GdipInst.CreateHBitmapFromBitmap(pBitmap)
GdipInst.DisposeImage(pBitmap)
Return hBitmap
}
GetImgResponseStream(url) {
whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("GET", url, false)
whr.Send()
if (whr.Status != 200) {
MsgBox, Failed to load the image!
Return
}
contentType := whr.GetResponseHeader("Content-Type")
if !InStr(contentType, "image") {
MsgBox, URL doesn't link to an image!
Return
}
Return whr.ResponseStream
}
class GDIp {
__New() {
if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
DllCall("LoadLibrary", "Str", "gdiplus")
VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
DllCall("gdiplus\GdiplusStartup", "UPtrP", pToken, "Ptr", &si, "Ptr", 0)
this.token := pToken
}
__Delete() {
DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
DllCall("FreeLibrary", "Ptr", hModule)
}
BitmapFromIStream(pIUnknown) {
static IID_IStream := "{0000000C-0000-0000-C000-000000000046}"
pIStream := ComObjQuery(pIUnknown, IID_IStream)
DllCall("gdiplus\GdipCreateBitmapFromStream", "Ptr", pIStream, "PtrP", pBitmap)
ObjRelease(pIStream)
Return pBitmap
}
CreateHBitmapFromBitmap(pBitmap) {
DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hBitmap, "UInt", 0xFFFFFFFF)
Return hBitmap
}
CreateBitmap(Width, Height, Format=0x26200A) {
DllCall("gdiplus\GdipCreateBitmapFromScan0", "Int", Width, "Int", Height, "Int", 0, "Int", Format, "Ptr", 0, "PtrP", pBitmap)
Return pBitmap
}
GraphicsFromImage(pBitmap) {
DllCall("gdiplus\GdipGetImageGraphicsContext", "Ptr", pBitmap, "PtrP", pGraphics)
return pGraphics
}
DrawImage(pGraphics, pBitmap, dx, dy, dw, dh, sx, sy, sw, sh) {
Return DllCall("gdiplus\GdipDrawImageRectRect", "Ptr", pGraphics, "Ptr", pBitmap
, "Float", dx, "Float", dy, "Float", dw, "Float", dh
, "Float", sx, "Float", sy, "Float", sw, "Float", sh
, "Int", 2, "Ptr", 0, "Ptr", 0, "Ptr", 0)
}
SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "")
{
; info — если копируем в буфер, тогда расширение файла, если в файл, тогда путь к файлу
if tobuff
Extension := info
else
SplitPath, info,,, Extension
if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
return -1
DllCall("gdiplus\GdipGetImageEncodersSize", "UintP", nCount, "UintP", nSize)
VarSetCapacity(ci, nSize)
DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
if !(nCount && nSize)
return -2
Loop, % nCount {
sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
if !InStr(sString, "*." Extension)
continue
pCodec := &ci+idx
break
}
if !pCodec
return -3
if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75 {
DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UintP", nSize)
VarSetCapacity(EncoderParameters, nSize, 0)
DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
Loop, % NumGet(EncoderParameters, "UInt")
{
elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6)
{
p := elem+&EncoderParameters-pad-4
NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
break
}
}
}
if !tobuff
E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "WStr", info, "Ptr", pCodec, "UInt", p ? p : 0)
else {
DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "PtrP", pStream )
if !E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBitmap, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 ) {
DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "PtrP", hData )
pData := DllCall( "GlobalLock", "Ptr", hData, "Ptr" )
nSize := DllCall( "GlobalSize", "Ptr", hData, "Ptr" )
VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "Ptr", nSize )
DllCall( "GlobalUnlock", "Ptr", hData )
DllCall( "GlobalFree", "Ptr", hData )
}
ObjRelease(pStream)
}
return E ? -4 : tobuff ? nSize : 0
}
DisposeImage(pBitmap) {
return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
}
DeleteGraphics(pGraphics) {
return DllCall("gdiplus\GdipDeleteGraphics", "Ptr", pGraphics)
}
}