page numbers: add/edit pagestamps, increment/decrement/get from Adobe Reader and WinDjView

Post your working scripts, libraries and tools
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

page numbers: add/edit pagestamps, increment/decrement/get from Adobe Reader and WinDjView

11 Nov 2017, 16:28

The basis of this script is that if you press Ctrl+Q it sends [p.1], if you press Ctrl+Q again it replaces this with [p.2], if you go down 2 lines to a blank line and press Ctrl+Q again it sends [p.3]. Ctrl+Shift+Q adds/edits a pagestamp, incrementing the number by 5. Similarly Ctrl+W and Ctrl+Shift+W decrement by 1 and 5 respectively.

It is designed to work in Notepad.

Other features:
- Create a pagestamp with the same number as the last number that was used.
- Instead of adding/editing a pagestamp and putting a cursor underneath, place the cursor to the right of the pagestamp.
- Create pagestamps using Roman numerals.
- Reset the page number in memory.
- Create a pagestamp based on the page number in Adobe Reader or WinDjView.
- Create a pagestamp based on the page number in Adobe Reader or WinDjView, with the document page number and the 'real' page number.

The script is taken from one of my main AHK v1.1 scripts which I've 99% converted to AHK v2, hence the 'AHK v2 functions for AHK v1'.

The functions JEE_MC and JEE_MX are equivalent to 'contains' and 'in'. They are short for 'match contains', and 'match exact'.

Code: Select all

;page numbers by jeeswg

#IfWinActive, ahk_class Notepad
^q:: ;notepad - page numbers - insert page number (with support for adobe reader + windjview)
^w:: ;notepad - page numbers - insert page number (with support for adobe reader + windjview)
^+q:: ;notepad - page numbers - insert page number (with support for adobe reader + windjview)
^+w:: ;notepad - page numbers - insert page number (with support for adobe reader + windjview)
^#!e:: ;notepad - page numbers - insert page number (with support for adobe reader + windjview)

;see lower down for an explanation of these variables
;vJeePageNumBetween := 1
;vExcludeList := "Untitled"

if vInProgress
	JEE_RetX()
vInProgress := 1

hWnd := WinGetID("A")
hCtl := ControlGetHwnd("Edit1", "ahk_id " hWnd)
vDHW := A_DetectHiddenWindows, DetectHiddenWindows("Off")
if hWnd2 := WinExist("ahk_class AcrobatSDIWindow")
{
	vUtil := "adobe"
	vNum := JEE_AdobeReaderGetPageNum(hWnd2)
}
else if vPID := ProcessExist("WinDjView.exe")
{
	hWnd2 := JEE_WinDjViewGetHwnd()
	vUtil := "wdjv"
	vNum := ControlGetText("Edit1", "ahk_id " hWnd2)
}
else
{
	vUtil := ""
	vNum := vPageNum
	;vJeePageNumBetween is useful
	;if you want to add page numbers to an existing document,
	;it will look above for the nearest pagestamp,
	;if the last output pagestamp was lower than the number above,
	;then the next output pagestamp will be one higher than the nearest pagestamp
	if vJeePageNumBetween
		vNum2 := vNum
}
DetectHiddenWindows(vDHW)

;if no pagestamp in memory, if no pdf browser open,
;check from caret position upwards for pagestamp
if (vUtil = "")
&& ((vNum = "") || vJeePageNumBetween)
{
	vText := ControlGetText("", "ahk_id " hCtl)
	if InStr(vText, "[p.")
	{
		hCtl := ControlGetHwnd("", "ahk_id " hCtl)
		JEE_EditGetSel(hCtl, vPos1, vPos2)
		vText := SubStr(vText, 1, vPos1)

		vNum := 0
		if (vPos1 := InStr(vText, "[p.", 0, 0))
		&& (vPos2 := InStr(vText, "]", 0, vPos1))
			vNum := SubStr(vText, vPos1+3, vPos2-vPos1-3)
		if !(vNum ~= "^\d+$")
			vNum := 0
		if vJeePageNumBetween && (vNum2 > vNum)
			vNum := vNum2
	}
	else
	{
		vNum := JEE_InputBox("page number:", 1) - 1
		if ErrorLevel
			JEE_RetX()
	}
}

if (vUtil = "")
{
	if InStr(A_ThisHotkey, "e")
		Sleep(0) ;(number stays the same)
	else if InStr(A_ThisHotkey, "+q")
		vNum += 5
	else if InStr(A_ThisHotkey, "q")
		vNum += 1
	else if InStr(A_ThisHotkey, "+w")
		vNum -= 5
	else if InStr(A_ThisHotkey, "w")
		vNum -= 1
	if (vNum < 1)
		vNum := 1
}

vPageNum := vNum
vOutput2 := "[p." vNum "]"
if (vUtil = "")
	vOutput := vOutput2
else
{
	if (vUtil = "adobe")
		vWinTitle := WinGetTitle("ahk_id " hWnd2)
	else if (vUtil = "wdjv")
		vWinTitle := WinGetTitle("ahk_id " hWnd2)
	;vPdfList needs to be defined at some point in the script
	;tab-separated with 3 values:
	;there are often two 'page ones' in a pdf/djvu,
	;page 1 of the file (virtual page one), page 1 of the physical book (real page one)
	;'real' page 1, contents page 1, filename (partial or full)
	;e.g. of vPdfList contents:
	;21	15	My Pdf Title
	vNum2 := JEE_CustomPageNumConvert(vNum, vWinTitle, vPdfList)
	if !(vNum = vNum2)
		vOutput2 := "[p." vNum2 "][pdf p." vNum "]"
	vOutput := vOutput2
}
if (vPageMode = "r") ;Roman numerals
{
	if !JEE_Between(vNum, 1, 3999)
		JEE_RetX()
	vOutput := "[p." JEE_NumToRom(vNum) "]"
}
vLen := StrLen(vOutput)

;==============================

;4 scenarios, where pipe represents the caret:

;if line above contains text but not a page no.
;and current line is blank
;then add page no. below (and go down)
;scenario AB ;add below
;TEXT      TEXT
;|      ->
;          [p.x]
;          |

;else if current line contains page no.
;then change page no. (and go down)
;scenario EC ;edit current
;[p.3]| -> [p.4]
;          |

;else if line above contains a page no.
;then change page no. above (and go down)
;scenario EA ;edit above
;[p.3]  -> [p.4]
;|         |

;else add page no. (and go down)
;scenario AC ;add current
;|      -> [p.4]
;          |

;==============================

if (vUtil = "")
{
	;change existing pagestamp / add new pagestamp
	;plus have cursor ready to type text on a blank line underneath pagestamp

	vLineCount := ControlGetLineCount("", "ahk_id " hCtl)
	vCurrentLineNum := ControlGetCurrentLine("", "ahk_id " hCtl)
	vLineAboveText := ControlGetLine(vCurrentLineNum-1, "", "ahk_id " hCtl)
	vCurrentLineText := ControlGetLine(vCurrentLineNum, "", "ahk_id " hCtl)

	if !InStr(vLineAboveText, "[p.")
	&& !(vLineAboveText = "")
	&& (vCurrentLineText = "")
		vChange := "AB"
	else if InStr(vCurrentLineText, "[p.")
		vChange := "EC"
	else if InStr(vLineAboveText, "[p.")
		vChange := "EA"
	else
		vChange := "AC"

	;if the Notepad window title contains text from the comma-separated exclude list,
	;then you can only edit/add pagestamps on the current line,
	;and the caret is placed to the right of the pagestamp
	vWinTitle := WinGetTitle("ahk_id " hWnd)
	if (!(vExcludeList = "") && JEE_MC(vWinTitle, vExcludeList))
	{
		vIsInExcludeList := 1
		if ((vChange = "EA") || (vChange = "AB"))
			vChange := "AC"
	}
	else
		vIsInExcludeList := 0

	;===============
	if (vChange = "AC") ;add pagestamp to current line
	{
		SendInput("{Home}" vOutput)
		if vIsInExcludeList
			SendInput("{Space}")
		else if (vLineCount = vCurrentLineNum) ;add line if required so that start on a blank line
			SendInput("{End}{Enter}")
		else
			SendInput("{Down}")
	}
	else if (vChange = "EA") ;change pagestamp on line above
	{
		vFindEndPagestamp := InStr(vLineAboveText, "]")
		vFindLenLine := StrLen(vLineAboveText)

		;e.g. '[p.9999]' (maximum allowed number)
		;mmmcmxcix (3999) (length: 9)
		;mmmdccclxxxviii (3888) (length: 15)
		if (vPageMode = "r")
			vRange := 19 ;[p.mmmdccclxxxviii]
		else
			vRange := 8 ;[p.9999]
		if !JEE_Between(vFindEndPagestamp, 4, vRange)
		{
			JEE_MsgBox("error " vChange " " vFindEndPagestamp " " vFindLenLine)
			JEE_RetX()
		}
		SendInput("{Up}")

		;if pagestamp is only text on line, select line
		;else, select just the first few characters on the line (that make up the pagestamp)
		if !(vFindEndPagestamp = vFindLenLine)
			JEE_PreventLargeTextSelect(vFindEndPagestamp)
		if (vFindEndPagestamp = vFindLenLine)
			SendInput("{Home}+{End}" vOutput)
		else
			SendInput("{Home}+{Right " vFindEndPagestamp "}" vOutput)
		SendInput("{Down}")
	}
	else if (vChange = "EC") ;change pagestamp on current line
	{
		vFindEndPagestamp := InStr(vCurrentLineText, "]")
		vFindLenLine := StrLen(vCurrentLineText)
		if JEE_MX(vPageMode, "r")
			vRange := 19
		else
			vRange := 8
		if !JEE_Between(vFindEndPagestamp, 4, vRange)
		{
			JEE_MsgBox("error " vChange " " vFindEndPagestamp " " vFindLenLine)
			JEE_RetX()
		}

		;if pagestamp is only text on line, select line
		;else, select just the first few characters on the line (that make up the pagestamp)
		if !(vFindEndPagestamp = vFindLenLine)
			JEE_PreventLargeTextSelect(vFindEndPagestamp)
		if (vFindEndPagestamp = vFindLenLine)
			SendInput("{Home}+{End}" vOutput)
		else
			SendInput("{Home}+{Right " vFindEndPagestamp "}" vOutput)
		if (vLineCount = vCurrentLineNum) ;add line if required so that start on a blank line
			SendInput("{End}{Enter}")
		else
			SendInput("{Down}")
		if vIsInExcludeList
			SendInput("{Home}{Left}")
	}
	else if (vChange = "AB") ;add pagestamp to line below
	{
		if (vLineCount = vCurrentLineNum) ;add line if required so that there is a line below
			SendInput("{End}{Enter}")
		else
			SendInput("{Down}")
		SendInput("{Home}" vOutput)
		if (vLineCount < vCurrentLineNum+2) ;add line if required so that start on a blank line
			SendInput("{End}{Enter}")
		else
			SendInput("{Down}")
	}
	;===============
}
if !(vUtil = "")
	SendInput(vOutput "{Enter}")
vInProgress := 0
JEE_RetX()

;==================================================

^+e:: ;notepad - page numbers - change settings (r/p), change num - ^+e
;page mode
;p: page number
;r: page number (Roman numerals)

hWnd := WinGetID("A")
vInput := JEE_InputBox("", vPageNum)
if (vInput = "p") || (vInput = "r")
	vPageMode := vInput
else
{
	vPageNum := vInput
	WinWaitActive("ahk_id " hWnd, "", 3)
	vOutput := "[p." vPageNum "]"
	SendInput(vOutput)
}
JEE_RetX()
#IfWinActive, ahk_class Notepad

;==================================================

;FUNCTIONS - PARTIAL FUNCTIONS

;==================================================

;note: not the full function (which supports large font)
JEE_MsgBox(vText:="Press OK to continue.", vTitle:="", vOpt:="")
{
	return MsgBox(vText, vTitle, vOpt)
}

;==================================================

;note: not the full function (which supports large font)
;note: parameter order is different to InputBox
;original: Text, Title, Options, DEFAULT
;modified: Text, DEFAULT, Title, Options
JEE_InputBox(vText:="", vDefault:="", vTitle:="", vOpt:="")
{
	return InputBox(vText, vTitle, vOpt, vDefault)
}

;==================================================

;note: not the full function (which records hotkey data)
JEE_RetX()
{
	Exit
}

;==================================================

;FUNCTIONS - FULL FUNCTIONS

;==================================================

JEE_AdobeReaderGetPageNum(hWnd)
{
	ControlGetText, vText, Edit2, % "ahk_id " hWnd
	;ControlGetText, vText, Edit5, % "ahk_id " hWnd
	return vText
}

;==================================================

;if vNum between %vRange1% and %vRange2%
JEE_Between(vNum, vRange1, vRange2)
{
	return (vNum >= vRange1 && vNum <= vRange2)
}

;==================================================

JEE_CustomPageNumConvert(vNum, vTitle, vList, vOpt:="")
{
	;default: pdf page number to real page number
	;c: get contents page number
	;r: real page number to pdf page number
	Loop, Parse, vList, `n, `r
	{
		vTemp := A_LoopField
		if (vTemp = "")
			continue
		Loop, Parse, vTemp, `t
			vTemp%A_Index% := A_LoopField
		if InStr(vOpt, "c") && InStr(vTitle, vTemp3)
			return vTemp2
		else if InStr(vOpt, "r") && InStr(vTitle, vTemp3)
			return (vNum + vTemp1 - 1)
		else if InStr(vTitle, vTemp3)
			return (vNum - vTemp1 + 1)
	}
	return vNum
}

;==================================================

;where vPos1 and vPos2 are 0-based
;e.g. JEE_EditGetSel(hCtl, vPos1, vPos2)
;vPos1=left, vPos2=right
;JEE_EditGetRange
JEE_EditGetSel(hCtl, ByRef vPos1, ByRef vPos2)
{
	VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
	SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0 ;(left, right)
	vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
}

;==================================================

;string contains one of comma-separated list
JEE_MC(ByRef vText, ByRef vNeedles)
{
	vDelim:=",", vCaseSen:=0, vMatch:=""
	if (vDelim = "")
		if InStr(vText, vNeedles, vCaseSen)
		{
			if IsByRef(vMatch)
				vMatch := vNeedles
			return 1
		}
		else
			return 0

	Loop, Parse, vNeedles, % vDelim
		if InStr(vText, A_LoopField, vCaseSen)
		{
			if IsByRef(vMatch)
				vMatch := A_LoopField
			return A_Index
		}
	return 0
}

;==================================================

;exact match, comma-separated list
JEE_MX(ByRef vText, ByRef vNeedles)
{
	vDelim:=",", vCaseSen:=0, vMatch:=""
	if (vDelim = "")
		if (!vCaseSen && ("" vText = vNeedles)) || (vCaseSen && ("" vText == vNeedles))
		{
			if IsByRef(vMatch)
				vMatch := vNeedles
			return 1
		}
		else
			return 0

	Loop, Parse, vNeedles, % vDelim
		if (!vCaseSen && ("" vText = A_LoopField)) || (vCaseSen && ("" vText == A_LoopField))
		{
			if IsByRef(vMatch)
				vMatch := A_LoopField
			return A_Index
		}
	return 0
}

;==================================================

JEE_NumToRom(vNum)
{
	static oArray := StrSplit(",i,ii,iii,iv,v,vi,vii,viii,ix,,x,xx,xxx,xl,l,lx,lxx,lxxx,xc,,c,cc,ccc,cd,d,dc,dcc,dccc,cm,,m,mm,mmm", ",")
	if (vNum < 1 || vNum > 3999)
	|| !(vNum ~= "^\d+")
		return
	vNum2 := Format("{:04}", vNum) ;make sure string is 4 characters long
	vRom := ""
	Loop, Parse, vNum2
	{
		vTemp := 40 - (A_Index*10) + A_LoopField + 1
		vRom .= oArray[vTemp]
	}
	return vRom
}

;==================================================

JEE_PreventLargeTextSelect(vNum, vLimit:=300)
{
	if (vNum > vLimit)
	{
		JEE_MsgBox("select text blocked:`r`n" "since character count greater than limit (" vNum " > " vLimit ")")
		Exit
	}
}

;==================================================

JEE_WinDjViewGetHwnd(vPID:=0)
{
	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows, On
	if !vPID
		WinGet, vWinList, List, ahk_exe WinDjView.exe
	else
		WinGet, vWinList, List, % "ahk_pid " vPID
	Loop, % vWinList
	{
		hWnd := vWinList%A_Index%
		WinGetClass, vWinClass, % "ahk_id " hWnd
		if (SubStr(vWinClass, 1, 4) = "Afx:")
		{
			DetectHiddenWindows, % vDHW
			return hWnd
		}
	}
	DetectHiddenWindows, % vDHW
}

;==================================================

;FUNCTIONS - SELECTED FORWARDS COMPATIBILITY FUNCTIONS
;commands as functions (AHK v2 functions for AHK v1) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=29689

;==================================================

ControlGetHwnd(Control:="", WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    ControlGet OutputVar, Hwnd,, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
ControlGetCurrentLine(Control:="", WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    ControlGet OutputVar, CurrentLine,, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
ControlGetLine(Index, Control:="", WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    ControlGet OutputVar, Line, %Index%, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
ControlGetLineCount(Control:="", WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    ControlGet OutputVar, LineCount,, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
ControlGetText(Control:="", WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    ControlGetText OutputVar, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    if !ErrorLevel
        return OutputVar
}
DetectHiddenWindows(OnOrOff)
{
    DetectHiddenWindows %OnOrOff%
}
InputBox(Text:="", Title:="", Options:="", Default:="")
{
    local _, _X, _Y, _W, _H, _T, _P, _Err
    ; v2 validates the value of a particular option:
    ; X and Y = integer (can be negative)
    ; W and H = postive integer only
    ; T = postive integer/float
    ; Credits to Lexikos [https://goo.gl/VjMTYu , https://goo.gl/ebEjon]
    RegExMatch(Options, "i)^[ \t]*(?:(?:X(?<X>-?\d+)|Y(?<Y>-?\d+)|W(?<W>\d+)"
        . "|H(?<H>\d+)|T(?<T>\d+(?:\.\d+)?)|(?<P>Password\S?)"
        . "|(?<Err>\S+)(*ACCEPT)"
        . ")(?=[ \t]|$)[ \t]*)*$", _)

    if (_Err != "")
        throw Exception("Invalid option.", -1, _Err)

    local OutputVar
    InputBox, OutputVar, %Title%, %Text%, % _P ? "HIDE" : "", %_W%, %_H%, %_X%, %_Y%,, %_T%, %Default%
    return OutputVar
}
MsgBox(Text:="Press OK to continue.", Title:="", Options:="")
{
    local Timeout, BoxType, k, Result, TimeMatch
    static BoxTypeArray:={"OK": 0, "O": 0, "OKCancel": 1, "OC": 1, "O/C": 1, "AbortRetryIgnore": 2, "ARI": 2, "A/R/I": 2
        , "YesNoCancel": 3, "Y/N/C": 3, "YNC": 3, "YesNo": 4, "Y/N": 4, "YN": 4, "RetryCancel": 5, "R/C": 5, "RC": 5
        , "CancelTryAgainContinue": 6, "C/T/C": 6, "CTC": 6, "Iconx": 16, "Icon?": 32, "Icon!": 48, "Iconi": 64
        , "Default2": 256, "Default3": 512, "Default4": 768}
    Timeout := "", BoxType := 0
    if (Options)
    {
        Loop, Parse, Options, % " `t"
            (k:=Abs(A_LoopField)) || (k:=BoxTypeArray[A_LoopField]) ? (BoxType|=k)
                : RegExMatch(A_LoopField, "Oi)^t(\d+\.?\d*)$", TimeMatch) ? Timeout:=TimeMatch.Value(1)
                : ("")
    }
    MsgBox % BoxType, % Title, % Text, % Timeout
    Loop Parse, % "Timeout,OK,Cancel,Yes,No,Abort,Ignore,Retry,Continue,TryAgain", % ","
        IfMsgBox % Result:=A_LoopField
            break
    return Result
}
ProcessExist(PIDorName:="")
{
    Process Exist, %PIDorName%
    return ErrorLevel
}
SendInput(Keys)
{
    SendInput %Keys%
}
Sleep(DelayInMilliseconds)
{
    Sleep %DelayInMilliseconds%
}
WinGetID(WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    WinGet OutputVar, ID, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar + 0
}
WinGetPID(WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    WinGet OutputVar, PID, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
WinGetTitle(WinTitle:="", WinText:="", ExcludeTitle:="", ExcludeText:="")
{
    local OutputVar
    WinGetTitle OutputVar, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
    return OutputVar
}
WinWaitActive(WinTitle:="", WinText:="", Seconds:="", ExcludeTitle:="", ExcludeText:="")
{
    WinWaitActive %WinTitle%, %WinText%, %Seconds%, %ExcludeTitle%, %ExcludeText%
    return !ErrorLevel
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Scripts and Functions”

Who is online

Users browsing this forum: robodesign and 27 guests