ComObject("HTMLfile") for extracting HTML Source from clipboard Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 18 Mar 2023, 23:48

Hi, all...

@oldbrother and @chatGPT came up with some clever code for putting HTML into the clipboard. I love this because it makes it easy to fill the clipboard with nicely formatted text generated by the script:

Code: Select all

html := "<html><body><h1>Hello World!</h1></body></html>"
ChatGPT_SetHtmlToClipboard(html)


ChatGPT_SetHtmlToClipboard(html) {
    htmlFile := ComObject("HTMLfile")
    htmlFile.write(html)
    bodyRange := htmlFile.body.createTextRange()
    bodyRange.select()
    bodyRange.execCommand("Copy")
    bodyRange := ""
    htmlFile := ""
}
I tried reversing the approach so I can copy some content in my browser (like from this forum) and then access the underlying source code that the clipboard uses to represent the content. I'd like to select some text and then have AHK extract a link and some text.

It didn't work. Can anyone fix it?

Here's what I've got:

Code: Select all

html := "<html><body><h1>Hello World!</h1></body></html>"
htmlFile := ComObject("HTMLfile")
htmlFile.write(html)
bodyRange := htmlFile.body.createTextRange()
bodyRange.select()
htmlFile.execCommand("Paste")
Sleep 500
output2 := htmlFile.tostring(&output1)
Msgbox "1=" output1 "`n`n2=" output2
I will be honest, COM is just one step to far for my programming skills, so I'm hoping someone else will find it easy.

Here's the documentation on HTMLFile. I couldn't make much sense out of it:
viewtopic.php?p=398#p398

User avatar
mikeyww
Posts: 26849
Joined: 09 Sep 2014, 18:38

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by mikeyww » 19 Mar 2023, 10:24

Here is something but would need to be converted to v2. viewtopic.php?p=67257#p67257

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard  Topic is solved

Post by teadrinker » 19 Mar 2023, 12:25

JoeSchmoe wrote: @chatGPT
Don't use chatGPT for AHK, it doesn't make sense. Perhaps you need this:

Code: Select all

F11:: {
    if CopySelectedText() {
        if html := ExtractHtmlData()
            url := RegExReplace(html, 's)(?<=href=")[^"]+(*SKIP)(*F)|.')
        MsgBox 'text: ' . A_Clipboard . '`nurl: ' . url
    }

    CopySelectedText() {
        ClipSaved := ClipboardAll()
        A_Clipboard := ''
        Send '^c'
        ClipWait 1
        if (A_Clipboard = '') {
            A_Clipboard := ClipSaved
            MsgBox 'Failed to copy data'
            return
        }
        return true
    }

    ExtractHtmlData() {
        static CF_HTML := DllCall('RegisterClipboardFormat', 'Str', 'HTML Format')
        DllCall('OpenClipboard', 'Ptr', A_ScriptHwnd)
        format := 0
        Loop {
            format := DllCall('EnumClipboardFormats', 'UInt', format)
        } until format = CF_HTML || format = 0
        if format != CF_HTML {
            DllCall('CloseClipboard')
            return
        }
        hData := DllCall('GetClipboardData', 'UInt', CF_HTML, 'Ptr')
        pData := DllCall('GlobalLock', 'Ptr', hData, 'Ptr')
        html := StrGet(pData, 'UTF-8')
        DllCall('GlobalUnlock', 'Ptr', hData)
        DllCall('CloseClipboard')
        return html
    }
}
Last edited by teadrinker on 20 Mar 2023, 14:49, edited 1 time in total.

User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 19 Mar 2023, 23:49

Many thanks, @mikeyww and especially @teadrinker. I tried converting the code mikeyww linked to, but the code Teadrinker provided seems to be the conversion I was looking for. I've added it to my code and it's working well.

If anyone has any similarly sleek code for going the other way (ie, putting HTML into the clipboard so you can paste it as rich text), I'd love to see it.
teadrinker wrote:
19 Mar 2023, 12:25
Don't use chatGPT for AHK, it doesn't make sense.
Fair enough... That was actually a reference to a snippet from viewtopic.php?f=83&t=114568 . It brings code the other way, but I gather it may be iffy.


teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 20 Mar 2023, 14:50

Replaced pData := DllCall('GlobalLock', 'Ptr', hData) with pData := DllCall('GlobalLock', 'Ptr', hData, 'Ptr').

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by FanaticGuru » 20 Mar 2023, 17:21

JoeSchmoe wrote:
19 Mar 2023, 23:49
If anyone has any similarly sleek code for going the other way (ie, putting HTML into the clipboard so you can paste it as rich text), I'd love to see it.
teadrinker wrote:
19 Mar 2023, 12:25
Don't use chatGPT for AHK, it doesn't make sense.
Fair enough... That was actually a reference to a snippet from viewtopic.php?f=83&t=114568 . It brings code the other way, but I gather it may be iffy.

This v1 code from SKAN seems to work with no modification: viewtopic.php?t=80706

I did modify the example code to work with v2:

Code: Select all

Hotstring(":*X:]d", DatePaste)

DatePaste(*)
{
	DTH := FormatTime(,"'<div>HTML: <i>' dddd '</i>', '<b>' dd - MMM - yyyy '</b>' h: mm tt '</div>'")
	DTT := FormatTime(,"TEXT: dddd, dd - MMM - yyyy h: mm tt")
	SetClipboardHTML(DTH, , DTT)
	SendInput "^v"
}


SetClipboardHTML(HtmlBody, HtmlHead := "", AltText := "") {       ; v0.67 by SKAN on D393/D42B
	Local F, Html, pMem, Bytes, hMemHTM := 0, hMemTXT := 0, Res1 := 1, Res2 := 1   ; @ tiny.cc/t80706
	Static CF_UNICODETEXT := 13, CFID := DllCall("RegisterClipboardFormat", "Str", "HTML Format")

	If !DllCall("OpenClipboard", "Ptr", A_ScriptHwnd)
		Return 0
	Else DllCall("EmptyClipboard")

	If (HtmlBody != "")
	{
		Html := "Version:0.9`r`nStartHTML:00000000`r`nEndHTML:00000000`r`nStartFragment"
			. ":00000000`r`nEndFragment:00000000`r`n<!DOCTYPE>`r`n<html>`r`n<head>`r`n"
			. HtmlHead . "`r`n</head>`r`n<body>`r`n<!--StartFragment -->`r`n"
			. HtmlBody . "`r`n<!--EndFragment -->`r`n</body>`r`n</html>"

		Bytes := StrPut(Html, "utf-8")
		hMemHTM := DllCall("GlobalAlloc", "Int", 0x42, "Ptr", Bytes + 4, "Ptr")
		pMem := DllCall("GlobalLock", "Ptr", hMemHTM, "Ptr")
		StrPut(Html, pMem, Bytes, "utf-8")

		F := DllCall("Shlwapi.dll\StrStrA", "Ptr", pMem, "AStr", "<html>", "Ptr") - pMem
		StrPut(Format("{:08}", F), pMem + 23, 8, "utf-8")
		F := DllCall("Shlwapi.dll\StrStrA", "Ptr", pMem, "AStr", "</html>", "Ptr") - pMem
		StrPut(Format("{:08}", F), pMem + 41, 8, "utf-8")
		F := DllCall("Shlwapi.dll\StrStrA", "Ptr", pMem, "AStr", "<!--StartFra", "Ptr") - pMem
		StrPut(Format("{:08}", F), pMem + 65, 8, "utf-8")
		F := DllCall("Shlwapi.dll\StrStrA", "Ptr", pMem, "AStr", "<!--EndFragm", "Ptr") - pMem
		StrPut(Format("{:08}", F), pMem + 87, 8, "utf-8")

		DllCall("GlobalUnlock", "Ptr", hMemHTM)
		Res1 := DllCall("SetClipboardData", "Int", CFID, "Ptr", hMemHTM)
	}

	If (AltText != "")
	{
		Bytes := StrPut(AltText, "utf-16")
		hMemTXT := DllCall("GlobalAlloc", "Int", 0x42, "Ptr", (Bytes * 2) + 8, "Ptr")
		pMem := DllCall("GlobalLock", "Ptr", hMemTXT, "Ptr")
		StrPut(AltText, pMem, Bytes, "utf-16")
		DllCall("GlobalUnlock", "Ptr", hMemTXT)
		Res2 := DllCall("SetClipboardData", "Int", CF_UNICODETEXT, "Ptr", hMemTXT)
	}

	DllCall("CloseClipboard")
	hMemHTM := hMemHTM ? DllCall("GlobalFree", "Ptr", hMemHTM) : 0

	Return (Res1 & Res2)
}
]d in Notepad and Word to see results.

It might be able to be improved for v2 but probably not much. It is basically just building a minimum HTML template then inserting the information into that template. With the addition of also allowing to set the plain text version in the clipboard. The clipboard can hold multiple types of information at once for pasting to different type locations. This information is often similar but does not have to be. You could paste one thing in a location that accepts HTML and then something totally different in a location that accepts only text. You could paste a copy of a picture file in explorer, a html link to the picture in Outlook, insert the actual picture in Word, the name of the picture with file size in Notepad, a video documentary about the picture in a media player, etc.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 21 Mar 2023, 14:15

My try:

Code: Select all

#Requires AutoHotkey v2

chunk1 := {text: 'Twinkle, twinkle, little star,`r`n',
           font: 'Harlow Solid Italic',
           size: 24,
           color: 0x1924B1}

chunk2 := {text: 'How I wonder what you are!`r`n',
           font: 'Harlow Solid Italic',
           size: 24,
           color: 0x4C57D8}

chunk3 := {text: 'Up above the world so high,`r`n',
           font: 'Harlow Solid Italic',
           size: 24,
           color: 0x7279D8}

chunk4 := {text: 'Like a diamond in the sky.',
           font: 'Harlow Solid Italic',
           size: 24,
           color: 0xFFBB00}

SetFormattedTextToClipboard(chunk1, chunk2, chunk3, chunk4)

SetFormattedTextToClipboard(chunks*) {
    static CF_UNICODETEXT := 13, LOGPIXELSY := 90, GHND := 0x00042
         , CF_HTML := DllCall('RegisterClipboardFormat', 'Str', 'HTML Format')
         , htmlHeader := 'Version:0.9`nStartHTML:-1`nEndHTML:-1`nStartFragment:00074`nEndFragment:00000`n'

    hDC := DllCall('GetDC', 'Ptr', 0, 'Ptr')
    plainText := html := ''
    for chunk in chunks {
        size := Round( DllCall('GetDeviceCaps', 'Ptr', hDC, 'Int', LOGPIXELSY) * chunk.size/72 * 96/A_ScreenDPI )
        utf8Buf := Buffer(StrPut(chunk.text, 'UTF-8'), 0)
        StrPut(chunk.text, utf8Buf, 'UTF-8')
        utf8Str := RegExReplace(StrGet(utf8Buf, 'cp0'), '\R', '<br>')
        color := Format('#{:06x}', chunk.color)
        html .= '<span style="font-family:' . chunk.font . ';font-size:' . size . 'px;color:' . color . ';">' . utf8Str . '</span>'
        plainText .= chunk.text
    }
    DllCall('ReleaseDC', 'Ptr', 0, 'Ptr', hDC)
    
    endFragmentOffset := Format('{:05u}', StrLen(htmlHeader . html))
    htmlHeader := StrReplace(htmlHeader, '00000', endFragmentOffset)
    htmlLen := StrPut(htmlHeader . html, 'cp0')
    unicodeLen := StrPut(plainText, "UTF-16")

    DllCall('OpenClipboard', 'Ptr', A_ScriptHwnd)
    DllCall('EmptyClipboard')
    Loop 2 {
        b := A_Index = 1
        hMem := DllCall('GlobalAlloc', 'UInt', GHND, 'Ptr', b ? htmlLen : unicodeLen, 'Ptr')
        pMem := DllCall('GlobalLock', 'Ptr', hMem, 'Ptr')
        StrPut(b ? htmlHeader . html : plainText, pMem, b ? 'cp0' : 'UTF-16')
        DllCall('GlobalUnlock', 'Ptr', hMem)
        DllCall('SetClipboardData', 'UInt', b ? CF_HTML : CF_UNICODETEXT, 'Ptr', hMem)
    }
    DllCall('CloseClipboard')
}
I wonder if CF_UNICODETEXT really needed? It works for me without it.
Last edited by teadrinker on 03 Apr 2023, 15:09, edited 3 times in total.

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by FanaticGuru » 21 Mar 2023, 17:05

teadrinker wrote:
21 Mar 2023, 14:15
My try:

He is a condensed version of @teadrinker code for just HTML to Clipboard:

Code: Select all

TD_Condensed_SetClipboardHTMLonly('<a href="https://www.autohotkey.com/">Linky to AutoHotkey</a>')

TD_Condensed_SetClipboardHTMLonly(html) {
    html := 'Version:0.9`nStartHTML:-1`nEndHTML:-1`nStartFragment:00074`nEndFragment:' Format('{:05u}', StrLen(html) + 74) '`n' html
	DllCall('OpenClipboard', 'Ptr', A_ScriptHwnd)
    DllCall('EmptyClipboard')
    hMem := DllCall('GlobalAlloc', 'UInt', 0x00042, 'Ptr', StrPut(html, 'cp0'), 'Ptr')
    StrPut(html, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'cp0')
    DllCall('SetClipboardData', 'UInt', DllCall('RegisterClipboardFormat', 'Str', 'HTML Format'), 'Ptr', hMem)
    DllCall('GlobalUnlock', 'Ptr', hMem)
    DllCall('GlobalFree', 'Ptr', hMem)
    DllCall('CloseClipboard')
}

No Header or Alternate Text, just HTML to Clipboard. If you attempt to paste anywhere not expecting HTML then you will paste nothing. Paste to Word, get nice link, paste to Notepad you get nothing.

With 3 more lines you could probably add alternate text but the point was to keep this function tight.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by FanaticGuru » 21 Mar 2023, 17:43

With alternate text:

Code: Select all

TD_Condensed_SetClipboardHTMLtext('<a href="https://www.autohotkey.com/">Linky to AutoHotkey</a>', 'https://www.autohotkey.com/')
TD_Condensed_SetClipboardHTMLtext(Html, Text := Html) 
{
	Html := 'Version:0.9`nStartHTML:-1`nEndHTML:-1`nStartFragment:00074`nEndFragment:' Format('{:05u}', StrLen(Html) + 74) '`n' Html
	DllCall('OpenClipboard', 'Ptr', A_ScriptHwnd)
	DllCall('EmptyClipboard')
	hMem := DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Html, 'cp0'), 'Ptr')
	StrPut(Html, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'cp0')
	DllCall('GlobalUnlock', 'Ptr', hMem)
	DllCall('SetClipboardData', 'UInt', DllCall('RegisterClipboardFormat', 'Str', 'HTML Format'), 'Ptr', hMem)
	hMem := DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Text, 'UTF-16') * 2, 'Ptr')
	StrPut(Text, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'UTF-16')
	DllCall('GlobalUnlock', 'Ptr', hMem)
	DllCall('SetClipboardData', 'UInt', 13, 'Ptr', hMem)
	DllCall('CloseClipboard')
}

I feel like I might should use GlobalUnlock for each GlobalLock but I am not sure. I am also not sure about the GlobalFree. GlobalFlags returns 1 so maybe only need to do them once like I did.


Went with double GlobalUnlock but no GlobalFree.

FG
Last edited by FanaticGuru on 21 Mar 2023, 22:17, edited 2 times in total.
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 21 Mar 2023, 19:03

Sorry for the noob question, but would the following be a good enough place to look up some of the DLL Functions being used here?
https://learn.microsoft.com/en-us/windows/win32/api/winuser/ (for functions with the word "clipboard" in their name)
https://learn.microsoft.com/en-us/windows/win32/api/winbase/ (for globallock/globalunlock)

As described in another thread, ""Send()" but for richer content," I'm planning on being a heavy user of this function for pasting rich text into a wide variety of applications. I'll also use teadrinker's original function for examining the results. (I'll copy the rich text into the clipboard, extract it using @teadrinker's function, and examine it to better understand how the text is represented in any given program). As a result, I'd love to better understand the underlying DLL functions.

EDIT: Nevermind. I checked the links and they are pretty solid. However, if anyone knows better links, please let me know!
Last edited by JoeSchmoe on 21 Mar 2023, 19:54, edited 1 time in total.

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 21 Mar 2023, 19:19

FanaticGuru wrote: If you attempt to paste anywhere not expecting HTML then you will paste nothing.
Ah, got it, thanks. However, if your goal is creating formatted text, you are unlikely to need plain or unicode text.
FanaticGuru wrote: I might should use GlobalUnlock for each GlobalLock but I am not sure. I am also not sure about the GlobalFree
As I understand, you should call GlobalFree after every call to GlobalAlloc, and GlobalUnlock after GlobalLock, MSDN is clear on this.

User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by kczx3 » 21 Mar 2023, 19:23

teadrinker wrote:
21 Mar 2023, 19:19
Ah, got it, thanks. However, if your goal is creating formatted text, you are unlikely to need plain or unicode text.
Far from true. Many applications specifically even support using Carl + Shift + V to paste plain/in formatted text. If you’re putting HTML on the clipboard then you should also put plain text on as well in my opinion.

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 21 Mar 2023, 19:28

Maybe, I only tested with MS Word.
JoeSchmoe wrote: Sorry for the noob question, but would the following be a good enough place to look up some of the DLL Functions being used here?
If I need to find some function on MSDN, I usually google something like "msdn [function name]".

User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by FanaticGuru » 21 Mar 2023, 19:46

teadrinker wrote:
21 Mar 2023, 19:19
As I understand, you should call GlobalFree after every call to GlobalAlloc, and GlobalUnlock after GlobalLock, MSDN is clear on this.

I tend to just over call cleanup type stuff as I am often unsure but according to this GlobalFree is not needed at all with SetClipboardData.

No, do not call GlobalFree after putting data on the clipboard. Think about it this way -- you are transferring ownership of the global memory block from your application to the clipboard. When the clipboard no longer needs the data, it will release the memory by calling GlobalFree itself.

https://learn.microsoft.com/en-us/answers/questions/607541/(clipboard)-do-i-need-to-call-globalfree-when-usin

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 21 Mar 2023, 19:56

teadrinker wrote:
21 Mar 2023, 19:28
Maybe, I only tested with MS Word.
JoeSchmoe wrote: Sorry for the noob question, but would the following be a good enough place to look up some of the DLL Functions being used here?
If I need to find some function on MSDN, I usually google something like "msdn [function name]".
Awesome.

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 21 Mar 2023, 20:22

Added CF_UNICODETEXT to my code.
FanaticGuru wrote: according to this GlobalFree is not needed at all with SetClipboardData
Perhaps it's true. I just tried running my function in a loop without GlobalFree and didn't notice any memory leaks. So I removed it from my code. :)

User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 21 Mar 2023, 23:43

The final boss would be combining this with Markdown in a way that would allow us to paste workable formatted text into websites like GMail, Google Docs, intranet sites, etc., that accept HTML.

As an experiment, I combined @teadrinker and @FanaticGuru's code with @TheArkive's MarkDown library.

It still needs a lot of refinement, but I thought I'd put it here in case anyone else wanted to play with it (and hopefully improve it!)

To send fairly complete nicely formatted markdown demo, just run the code and type mddemo into any program that accepts HTML. I like using it in the left-side of the following site, because it mostly gets the formatting right and you can see the HTML on the right: https://htmlg.com/html-editor/

Code: Select all

; The following is just a demo of combining code by TheArkive and SKAN/teadrinker/FanaticGuru.
; Very little of the following is my own work.
; It just combines two scripts:
; 1.) For putting HTML in the clipboard, SetClipboardHTMLtext, by teadrinker and FanaticGuru:
; https://www.autohotkey.com/boards/viewtopic.php?f=82&t=115178
; 2.) For transforming Markdown into HTML, M-ArkDown (make_html) by TheArkive:
; https://github.com/TheArkive/M-ArkDown_ahk2/blob/master/_MD_Gen.ahk


::mddemo::{
	MDSend(SampleText())

	A_Clipboard := ""
	A_Clipboard := "`n`nHTML=`n" make_html(SampleText(), options)
	ClipWait(1, True)
	Send '^v'
}
^r::Reload

css:= TheArkiveCSS() ;"p {margin: 0;} html, body {font-family: [_font_name_];}"
options := {css:css, font_name:"sans-serif", font_size:12, font_weight:400}

MDSend(MarkDownText, PlainText := MarkDownText) {
	; Sendmode = Paste :-)
	SavedClip := ClipboardAll()
	A_Clipboard := ""
	TD_Condensed_SetClipboardHTMLtext(make_html(MarkDownText, options), PlainText)
	If ClipWait(1, True) {
		Send '^v'
	} Else MsgBox 'An error occurred while waiting for the clipboard.', 'Error', 48
	Sleep 150
	A_Clipboard := SavedClip
	SavedClip := ""
}



TD_Condensed_SetClipboardHTMLtext(Html, Text := Html)
{
	Html := 'Version:0.9`nStartHTML:-1`nEndHTML:-1`nStartFragment:00074`nEndFragment:' Format('{:05u}', StrLen(Html) + 74) '`n' Html
	DllCall('OpenClipboard', 'Ptr', A_ScriptHwnd)
	DllCall('EmptyClipboard')
	hMem := DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Html, 'cp0'), 'Ptr')
	StrPut(Html, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'cp0')
	DllCall('GlobalUnlock', 'Ptr', hMem)
	DllCall('SetClipboardData', 'UInt', DllCall('RegisterClipboardFormat', 'Str', 'HTML Format'), 'Ptr', hMem)
	hMem := DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Text, 'UTF-16') * 2, 'Ptr')
	StrPut(Text, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'UTF-16')
	DllCall('GlobalUnlock', 'Ptr', hMem)
	DllCall('SetClipboardData', 'UInt', 13, 'Ptr', hMem)
	DllCall('CloseClipboard')
}


; ================================================
; make_html(_in_html, options_obj:="", github:=false)
;
;   Ignore the last 2 params.  Those are used internally.
;
;   See above for constructing the options_obj.
;
;   The "github" param is a work in progress, and tries to enforce some of the expected basics
;   that are circumvented with my "flavor" of markdown.
;
;       Current effects when [ github := true ]:
;           * H1 and H2 always have underline (the [underline] tag still takes effect when specified)
;           * the '=' is not usable for making an <hr>, but --- *** and ___ still make <hr>
;
; ================================================

/*
MIT License

make_html() and TheArkive CSS Copyright (c) 2021 TheArkive https://github.com/TheArkive/M-ArkDown_ahk2

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/


make_html(_in_text, options:="", github:=false, final:=true, md_type:="") {

    If !RegExMatch(_in_text,"[`r`n]+$") && (final) && md_type!="header" { ; add trailing CRLF if doesn't exist
        _in_text .= "`r`n"
    }

    html1 := "<html><head><style>`r`n"
    html2 := "`r`n</style></head>`r`n`r`n<body>"
    toc_html1 := '<div id="toc-container">'
               . '<div id="toc-icon" align="right">&#9776;</div>'
               . '<div id="toc-contents">'
    toc_html2 := "</div></div>" ; end toc-container and toc-contents
    html3 := '<div id="body-container"><div id="main">`r`n' ; <div id=" q "body-container" q ">
    html4 := "</div></div></body></html>" ; </div>

    body := ""
    toc := [], do_toc := false
    do_nav := false, nav_arr := []

    table_done := false

    If (final)
        css := options.css

    a := StrSplit(_in_text,"`n","`r")
    i := 0

    While (i < a.Length) {                                          ; ( ) \x28 \x29
        i++, line := a[i]                                           ; [ ] \x5B \x5D
        blockquote := "", ul := "", ul2 := "", code_block := ""     ; { } \x7B \x7D
        ol := "", ol2 := "", ol_type := ""
        table := ""

        If final && RegExMatch(line, "^<nav\|") && a.Has(i+1) && (a[i+1] = "") {
            do_nav := True
            nav_arr := StrSplit(Trim(line,"<>"),"|")
            nav_arr.RemoveAt(1)
            Continue
        }

        If (final && line = "<toc>") {
            do_toc := True
            Continue
        }

        ; header h1 - h6
        If RegExMatch(line, "^(#+) (.+)", &match) {
            ; dbg("HEADER H1-H6")

            depth := match[1], _class := "", title := ltgt(match[2])

            If RegExMatch(line, "\x5B *([\w ]+) *\x5D$", &_match)
                _class := _match[1], title := SubStr(title, 1, StrLen(match[2]) - _match.Len(0))

            If (github && (match.Len(1) = 1 || match.Len(1) = 2))
                _class := "underline"

            id := RegExReplace(RegExReplace(StrLower(title),"[\[\]\{\}\(\)\@\!]",""),"[ \.]","-")
            opener := "<h" match.Len(1) (id?' id="' id '" ':'') (_class?' class="' _class '"':'') '>'

            body .= (body?"`r`n":"") opener Trim(make_html(title,,github,false,"header"),"`r`n")
            ; body .= (body?"`r`n":"") opener title
                  . '<a href="#' id '"><span class="link">•</span></a>'
                  . "</h" match.Len(1) ">"

            toc.Push([StrLen(depth), title, id])
            Continue
        }

        ; spoiler
        If RegExMatch(line, "^<spoiler=([^>]+)>$", &match) {
            disp_text := ltgt(match[1])
            spoiler_text := ""
            i++, line := a[i]
            While !RegExMatch(line, "^</spoiler>$") {
                spoiler_text .= (spoiler_text?"`r`n":"") line
                i++, line := a[i]
            }

            body .= (body?"`r`n":"") '<p><details><summary class="spoiler">'
                  . disp_text "</summary>" make_html(spoiler_text,,github,false,"spoiler") "</details></p>"
            Continue
        }

        ; hr
        If RegExMatch(line, "^([=\-\*_ ]{3,}[=\-\*_ ]*)(?:\x5B *[^\x5D]* *\x5D)?$", &match) {
            ; dbg("HR: " line)

            hr_style := ""

            If Trim(line)=""
                Continue

            If (github && SubStr(match[1],1,1) = "=")
                Continue

            If RegExMatch(line, "\x5B *([^\x5D]*) *\x5D", &match) {
                hr_str := match[1]
                arr := StrSplit(hr_str," ")

                For i, style in arr {
                    If (SubStr(style, -2) = "px")
                        hr_style .= (hr_style?" ":"") "border-top-width: " style ";"
                    Else If RegExMatch(style, "(dotted|dashed|solid|double|groove|ridge|inset|outset|none|hidden)")
                        hr_style .= (hr_style?" ":"") "border-top-style: " style ";"
                    Else
                        hr_style .= (hr_style?" ":"") "border-top-color: " style ";"
                }
            }
            body .= (body?"`r`n":"") '<hr style="' hr_style '">'
            Continue
        }

        ; blockquote - must come earlier because of nested elements
        While RegExMatch(line, "^\> ?(.*)", &match) {
            ; dbg("BLOCKQUOTE 1")

            blockquote .= (blockquote?"`r`n":"") match[1]

            If a.Has(i+1)
                i++, line := Trim(a[i]," `t")
            Else
                Break
        }

        If (blockquote) {
            ; dbg("BLOCKQUOTE 2")

            body .= (body?"`r`n":"") "<blockquote>" make_html(blockquote,,github, false, "blockquote") "</blockquote>"
            Continue
        }

        ; code block
        If (line = "``````") {
            ; dbg("CODEBLOCK")

            If (i < a.Length)
                i++, line := a[i]
            Else
                Break

            While (line != "``````") {
                code_block .= (code_block?"`r`n":"") line
                If (i < a.Length)
                    i++, line := a[i]
                Else
                    Break
            }

            body .= (body?"`r`n":"") "<pre><code>" StrReplace(StrReplace(code_block,"<","&lt;"),">","&gt;") "</code></pre>"
            Continue
        }

        ; table
        While RegExMatch(line, "^\|.*?\|$") {
            ; dbg("TABLE 1")

            table .= (table?"`r`n":"") line

            If a.Has(i+1)
                i++, line := a[i]
            Else
                Break
        }

        If (table) {
            ; dbg("TABLE 2")

            table_done := true

            body .= (body?"`r`n":"") '<table class="normal">'
            b := [], h := [], t := " `t"

            Loop Parse table, "`n", "`r"
            {
                body .= "<tr>"
                c := StrSplit(A_LoopField,"|"), c.RemoveAt(1), c.RemoveAt(c.Length)

                If (A_Index = 1) {
                    align := ""
                    Loop c.Length {
                        If RegExMatch(Trim(c[A_Index],t), "^:(.+?)(?<!\\):$", &match) {
                            m := StrReplace(inline_code(match[1]),"\:",":")
                            h.Push(["center",m])
                        } Else If RegExMatch(Trim(c[A_Index],t), "^([^:].+?)(?<!\\):$", &match) {
                            m := StrReplace(inline_code(match[1]),"\:",":")
                            h.Push(["right",m])
                        } Else If RegExMatch(Trim(c[A_Index],t), "^:(.+)", &match) {
                            m := StrReplace(inline_code(match[1]),"\:",":")
                            h.Push(["left",m])
                        } Else {
                            m := StrReplace(inline_code(Trim(c[A_Index],t)),"\:",":")
                            h.Push(["",m])
                        }
                    }
                } Else If (A_Index = 2) {
                    Loop c.Length {
                        If RegExMatch(c[A_Index], "^:\-+:$", &match)
                            b.Push(align:="center")
                        Else If RegExMatch(c[A_Index], "^\-+:$", &match)
                            b.Push(align:="right")
                        Else
                            b.Push(align:="left")
                        If (!h[A_Index][1])
                            h[A_Index][1] := align
                        body .= '<th align="' h[A_Index][1] '">' h[A_Index][2] '</th>'
                    }
                } Else {
                    Loop c.Length {
                        m := inline_code(c[A_Index]) ; make_html(c[A_Index],, false, "table data")
                        body .= '<td align="' b[A_Index]  '">' Trim(m," `t") '</td>'
                    }
                }
                body .= "</tr>"
            }
            body .= "</table>"
            Continue
        }

        ; unordered lists
        If RegExMatch(line, "^( *)[\*\+\-] (.+?)(\\?)$", &match) {

            ; dbg("UNORDERED LISTS")

            While RegExMatch(line, "^( *)([\*\+\-] )?(.+?)(\\?)$", &match) { ; previous IF ensures first iteration is a list item
                ul2 := ""

                If !match[1] && match[2] && match[3] {
                    ul .= (ul?"</li>`r`n":"") "<li>" make_html(match[3],,github,false,"ul item")

                    If match[4]
                        ul .= "<br>"

                    If (i < a.Length)
                        i++, line := a[i]
                    Else
                        Break

                    Continue

                } Else If !match[2] && match[3] {
                    ul .= make_html(match[3],,github,false,"ul item append")

                    If match[4]
                        ul .= "<br>"

                    If (i < a.Length)
                        i++, line := a[i]
                    Else
                        Break

                    Continue

                } Else If match[1] && match[3] {

                    While RegExMatch(line, "^( *)([\*\+\-] )?(.+?)(\\?)$", &match) {
                        If (Mod(StrLen(match[1]),2) || !match[1] || !match[3])
                            Break

                        ul2 .= (ul2?"`r`n":"") SubStr(line, 3)

                        If (i < a.Length)
                            i++, line := a[i]
                        Else {
                            line := ""
                            Break
                        }
                    }

                    ul .= "`r`n" make_html(ul2,,github,false,"ul")
                    Continue
                }

                If (i < a.Length)
                    i++, line := a[i]
                Else
                    Break
            }
        }

        If (ul) {
            body .= (body?"`r`n":"") "<ul>`r`n" ul "</li></ul>`r`n"
            Continue
        }

        ; ordered lists
        If RegExMatch(line, "^( *)[\dA-Za-z]+(?:\.|\x29) +(.+?)(\\?)$", &match) {

            ; dbg("ORDERED LISTS")

            While RegExMatch(line, "^( *)([\dA-Za-z]+(?:\.|\x29) )?(.+?)(\\?)$", &match) { ; previous IF ensures first iteration is a list item
                ol2 := ""

                If !match[1] && match[2] && match[3] {
                    ol .= (ol?"</li>`r`n":"") "<li>" make_html(match[3],,github,false,"ol item")

                    If (A_Index = 1)
                        ol_type := 'type="' RegExReplace(match[2], "[\.\) ]","") '"'

                    If match[4]
                        ol .= "<br>"

                    If (i < a.Length)
                        i++, line := a[i]
                    Else
                        Break

                    Continue

                } Else If !match[2] && match[3] {
                    ol .= make_html(match[3],,github,false,"ol item append")

                    If match[4]
                        ol .= "<br>"

                    If (i < a.Length)
                        i++, line := a[i]
                    Else
                        Break

                    Continue

                } Else If match[1] && match[3] {

                    While RegExMatch(line, "^( *)([\dA-Za-z]+(?:\.|\x29) )?(.+?)(\\?)$", &match) {
                        If (Mod(StrLen(match[1]),2) || !match[1] || !match[3])
                            Break

                        ol2 .= (ol2?"`r`n":"") SubStr(line, 3)

                        If (i < a.Length)
                            i++, line := a[i]
                        Else {
                            line := ""
                            Break
                        }
                    }

                    ol .= "`r`n" make_html(ol2,,github,false,"ol")
                    Continue
                }

                If (i < a.Length)
                    i++, line := a[i]
                Else
                    Break
            }
        }

        If (ol) {
            body .= (body?"`r`n":"") "<ol " ol_type ">`r`n" ol "</li></ol>`r`n"
            Continue
        }

        ; =======================================================================
        ; ...
        ; =======================================================================

        If RegExMatch(md_type,"^(ol|ul)") { ; ordered/unordered lists
            body .= (body?"`r`n":"") inline_code(line)
            Continue
        } Else If RegExMatch(line, "^(<nav|<toc)") { ; nav toc
            Continue
        } Else If RegExMatch(line, "\\$") { ; manual line break at end \
            body .= (body?"`r`n":"") "<p>"
            reps := 0

            While RegExMatch(line, "(.+)\\$", &match) {
                reps++
                body .= ((A_Index>1)?"<br>":"") inline_code(match[1])

                If (i < a.Length)
                    i++, line := a[i]
                Else
                    Break
            }

            If line
                body .= (reps?"<br>":"") inline_code(line) "</p>"
            Else
                body .= "</p>"
        } Else If line {
            If md_type != "header"
                body .= (body?"`r`n":"") "<p>" inline_code(line) "</p>"
            Else
                body .= (body?"`r`n":"") inline_code(line)
        }
    }

    ; processing toc ; try to process exact height
    final_toc := "", toc_width := 0, toc_height := 0
    If (Final && do_toc) {
        temp := Gui()
        temp.SetFont("s" options.font_size, options.font_name)

        depth := toc[1][1]
        diff := (depth > 1) ? depth - 1 : 0
        indent := "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"

        For i, item in toc { ; 1=depth, 2=title, 3=id
            depth := item[1] - diff - 1

            ctl := temp.Add("Text",, rpt("     ",depth) "• " item[2])
            ctl.GetPos(,,&w, &h)
            toc_width := (w > toc_width) ? w : toc_width
            toc_height += options.font_size * 2

            final_toc .= (final_toc?"`r`n":"") '<a href="#' item[3] '">'
                       . '<div class="toc-item">' (depth?rpt(indent,depth):"")
                       . "• " ltgt(item[2]) "</div></a>"
        }

        temp.Destroy()
    }

    ; processing navigation menu
    nav_str := ""
    If (final && do_nav) {
        temp := Gui()
        temp.SetFont("s" options.font_size, options.font_name)

        Loop nav_arr.Length {
            title := SubStr((txt := nav_arr[A_Index]), 1, (sep := InStr(txt, "=")) - 1)

            ctl := temp.Add("Text",,title)
            ctl.GetPos(,,&w)
            toc_width := (w > toc_width) ? w : toc_width
            toc_height += options.font_size * 2

            nav_str .= (final_toc?"`r`n":"") '<a href="' SubStr(txt, sep+1) '" target="_blank" rel="noopener noreferrer">'
                       . '<div class="toc-item">' title '</div></a>'
        }

        (do_toc) ? nav_str .= "<hr>" : ""
        temp.Destroy()
    }

    ; processing TOC
    user_menu := ""
    If Final && (do_nav || do_toc)
        user_menu := toc_html1 nav_str final_toc toc_html2

    If final {
        If (do_nav && do_toc)
            toc_height += Round(options.font_size * 1.6) ; multiply by body line-height

        css := StrReplace(css, "[_toc_width_]",toc_width + 25) ; account for scrollbar width
        css := StrReplace(css, "[_toc_height_]",toc_height)
        css := StrReplace(css, "[_font_name_]", options.font_name)
        css := StrReplace(css, "[_font_size_]", options.font_size)
        css := StrReplace(css, "[_font_weight_]", options.font_weight)

        If (do_toc || do_nav)
            result := html1 . css . html2 . user_menu . html3 . body . html4
        Else
            result := html1 . css . html2 . html3 . body . html4
    } Else
        result := body

    return result

    ; =======================================================================
    ; Local Functions
    ; =======================================================================

    inline_code(_in) {
        output := _in

        ; inline code
        While RegExMatch(output, "``(.+?)``", &match) {
            output := StrReplace(output, match[0], "<code>" ltgt(match[1]) "</code>",,,1)
        }

        ; image
        r := 1
        While (s := RegExMatch(output, "!\x5B *([^\x5D]*) *\x5D\x28 *([^\x29]+) *\x29(\x28 *[^\x29]* *\x29)?", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            dims := Trim(match[3],"()")
            output := StrReplace(output, match[0], '<img src="' match[2] '"' (dims?" " dims:"")
                    . ' alt="' ltgt(match[1]) '" title="' ltgt(match[1]) '">',,,1)
        }

        ; link / url
        r := 1
        While (s := RegExMatch(output, "\x5B *([^\x5D]+) *\x5D\x28 *([^\x29]+) *\x29", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            output := StrReplace(output, match[0], '<a href="' match[2] '" target="_blank" rel="noopener noreferrer">'
                    . match[1] "</a>",,,1)
        }

        ; strong + emphesis (bold + italics)
        While (s := RegExMatch(output, "(?<!\w)[\*]{3,3}([^\*]+)[\*]{3,3}", &match, r))
           || (s := RegExMatch(output, "(?<!\w)[\_]{3,3}([^\_]+)[\_]{3,3}", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            output := StrReplace(output, match[0], "<em><strong>" ltgt(match[1]) "</strong></em>",,,1)
        }

        ; strong (bold)
        While (s := RegExMatch(output, "(?<!\w)[\*]{2,2}([^\*]+)[\*]{2,2}", &match, r))
           || (s := RegExMatch(output, "(?<!\w)[\_]{2,2}([^\_]+)[\_]{2,2}", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            output := StrReplace(output, match[0], "<strong>" ltgt(match[1]) "</strong>",,,1)
        }

        ; emphesis (italics)
        While (s := RegExMatch(output, "(?<!\w)[\*]{1,1}([^\*]+)[\*]{1,1}", &match, r))
           || (s := RegExMatch(output, "(?<!\w)[\_]{1,1}([^\_]+)[\_]{1,1}", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            output := StrReplace(output, match[0], "<em>" ltgt(match[1]) "</em>",,,1)
        }

        ; strikethrough
        While (s := RegExMatch(output, "(?<!\w)~{2,2}([^~]+)~{2,2}", &match, r)) {
            If IsInCode(match[0], output) || IsInTag(match[0], output) {
                r := s + match.Len(0)
                Continue
            }
            output := StrReplace(output, match[0], "<del>" ltgt(match[1]) "</del>",,,1)
        }

        return output
    }

    ltgt(_in) {
        return StrReplace(StrReplace(_in,"<","&lt;"),">","&gt;")
    }

    rpt(_in, reps) {
        final_str := ""         ; Had to change "final" var to "final_str".
        Loop reps               ; This may still be a bug in a133...
            final_str .= _in
        return final_str
    }

    IsInTag(needle, haystack) {
        start := InStr(haystack, needle) + StrLen(needle)
        sub_str := SubStr(haystack, start)

        tag_start := InStr(sub_str,"<")
        tag_end := InStr(sub_str,">")

        If (!tag_start && tag_end) Or (tag_end < tag_start)
            return true
        Else
            return false
    }

    IsInCode(needle, haystack) {
        start := InStr(haystack, needle) + StrLen(needle)
        sub_str := SubStr(haystack, start)

        code_start := InStr(sub_str,"<code>")
        code_end := InStr(sub_str,"</code>")

        If (!code_start && code_end) Or (code_end < code_start)
            return true
        Else
            return false
    }
}



TheArkiveCSS(){
stylecss := "
(
:root{
    --link: #00ACAF;
    --link-hover: cyan;
    --link-special: orange;
    --link-visited: #006bcf;
    --text: #999;
    --header: #00ACAF;
    --border: #00ACAF;
    --blockquote: #0000FF;
    --blockquote-text: #DDD;
    --row-HL: #000C67;
    --bg: #000C17;
    --bg1-5: #000C27;
    --bg2: #000C37;
    --bg3: #000C57;
    --so1: yellow;
    --so2: orange;
    --so3: red;
    --so4: magenta;
    --emphasis: #EEE;
}

html, body {
    background-color: var(--bg);
    font-family: [_font_name_];
    font-size: [_font_size_]px;
    font-weight: [_font_weight_];
}

body {
    color: var(--text);
    line-height: 1.6em;
}

#body-container {
    width: 100%;
    height: 100%;
}

#main {
    max-width: 1280px;
    margin: auto;
    height: 90%;
    padding: 45px 45px;
    overflow-y: scroll;
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
}

#toc-container {
    background-color: var(--bg);
    overflow: hidden;
    position: fixed;
    display: block;
    float: right;
    right: 30px;
    top: 0px;
    width: 30px;
    height: 30px;
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 10px 10px;
    transition-duration: 0.25s;
}

@media screen and (min-width: 1383px) {
    #toc-container { right: calc(50% - 640px - 25px); }
}

#toc-icon {
    font-size: 24px;
    padding-right: 5px;
    color: var(--link);
}

#toc-contents {
    overflow: hidden;
    display: none;
}

.toc-item {
    padding: 2px 15px;
}

#toc-container:hover {
    overflow-y: auto;
    width: [_toc_width_]px;
    height: [_toc_height_]px;
    max-height: calc(100vh - 40px);
    transition-duration: 0.25s;
}

#toc-container:hover #toc-contents {
    display: block;
}

#toc-container:hover a {
    text-decoration: none;
}

#toc-container:hover .toc-item:hover {
    background-color: var(--row-HL);
    color: cyan;
}

:target a:visited { color: var(--so4); }
:target a:visited:hover, :target a:visited:hover .link { color: var(--link-hover); }
:target.underline { border-color: var(--so4); }

img { opacity: 0.5; }

table.normal tr th { background: var(--bg3); color: var(--header); }
table.normal tr td { background: var(--bg2); }
table.normal tr th, table tr td { padding: 5px; }
table.normal tr:hover td { background: var(--row-HL); }

a, a:link { color: var(--link); }
a:hover { color: var(--link-hover); text-decoration: underline; }
a:visited { color: var(--link); }

h1, h2, h3, h4, h5, h6
{ font-weight:bold; color:var(--header); margin-top: 24px; margin-bottom: [_font_size_]px; }

h1 a, h2 a, h3 a, h4 a, h5 a, h6 a,
h1:hover a, h2:hover a, h3:hover a, h4:hover a, h5:hover a, h6:hover a
{ text-decoration: none; }

h1 a .link, h2 a .link, h3 a .link, h4 a .link, h5 a .link, h6 a .link
{ position: relative; float: left; margin-left: -20px; display: none; }

h1:hover, h2:hover, h3:hover, h4:hover, h5:hover, h6:hover
{ border-color: var(--link-hover); color: var(--link-hover); }

h1:hover a .link, h2:hover a .link, h3:hover a .link, h4:hover a .link, h5:hover a .link, h6:hover a .link
{ display: inline; }

.underline { border-bottom: 1px solid #00ACAF; }
h1.underline { padding-bottom: 10px; }
h2.underline { padding-bottom: 8px; }
h3.underline { padding-bottom: 6px; }
h4.underline { padding-bottom: 4px; }
h5.underline { padding-bottom: 2px; }
h1 { font-size:1.75em; }
h2 { font-size:1.5em; }
h3 { font-size:1.30em; }
h4 { font-size:1.15em; }
h5 { font-size:1em; }
h6 { font-size:0.75em; }

em, strong { color: var(--emphasis); }
del { font-weight: 400; }
hr {
    color: transparent;
    border-top-style: solid;
    border-top-width: 1px;
    border-top-color: var(--border);
}

blockquote {
    border-left: 4px solid var(--blockquote);
    padding: 0 15px;
    color: var(--blockquote-text);
    margin-top: 0px;
    margin-bottom: [_font_size_]px;
}

p { margin-bottom: [_font_size_]px; margin-top: 0px; }

pre {
    margin-top: 10px;
    margin-bottom: [_font_size_]px;
    background-color: var(--b1-5);
    padding: 2px 4px;
    border: 1px solid var(--so1);
    border-radius: 5px;
    font-family: Consolas;
    font-size: [_font_size_]px;
}

code {
    margin: 0px 3px 0px 0px;
    padding: 2px 4px;
    background-color: var(--b1-5);
    border: 1px solid var(--border);
    border-radius: 5px;
    font-family: Consolas;
    font-size: [_font_size_]px;
    font-weight: 100;
}

pre code { border:none; padding:0px; }

li { margin: 4px 0; }

.header-menu {
  position: fixed;
  background-color: var(--bg2);
  top: 0px;
  max-width: 1280px;
  display: inline-block;
}
.header-menu-cell { border: none; }
.header-menu-cell {
  border-right: 1px solid var(--bg);
  padding: 10px 20px;
}
.header-menu-cell:hover {
  background-color: var(--row-HL);
}
.header-menu-cell:hover a,
.header-menu-cell a
{ text-decoration: none; }

.spoiler {
    color: var(--so2)
}

summary { margin-bottom: [_font_size_]px; }
)"
Return stylecss
}



SampleText(){
SyntaxGuide := "
(
# Markdown syntax guide

## Headers

# This is a Heading h1
## This is a Heading h2
### This is a Heading h3
#### This is a Heading h4
##### This is a Heading h5
###### This is a Heading h6

## Emphasis

*This text will be italic*
_This will also be italic_

**This text will be bold**
__This will also be bold__

_You **can** combine them_

## Lists

### Unordered

* Item 1
* Item 2
* Item 2a
* Item 2b

### Ordered

1. Item 1
1. Item 2
1. Item 3
  1. Item 3a
  1. Item 3b

## Images

![AutoHotkey Logo](https://www.autohotkey.com/static/ahk_logo_no_text.svg "AutoHotkey Logo.")

## Links

Help docs can be found [here](https://www.autohotkey.com/docs/).

## Blockquotes

> Markdown is a lightweight markup language with plain-text-formatting syntax, created in 2004 by John Gruber with Aaron Swartz.
>
>> Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor.

## Tables

| Left columns  | Right columns |
| ------------- |:-------------:|
| left foo      | right foo     |
| left bar      | right bar     |
| left baz      | right baz     |

## Blocks of code

```
let message = 'Hello world';
alert(message);
```

## Inline code

This is created with `make_html()` and `PutHTMLonClipboard()`.
)"
Return SyntaxGuide
}

User avatar
JoeSchmoe
Posts: 129
Joined: 08 Dec 2014, 08:58

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by JoeSchmoe » 03 Apr 2023, 13:52

FanaticGuru wrote:
21 Mar 2023, 17:43

Code: Select all

TD_Condensed_SetClipboardHTMLtext(Html, Text := Html) {
	<snip>
	hMem := DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Text, 'UTF-16') * 2, 'Ptr')
	StrPut(Text, DllCall('GlobalLock', 'Ptr', hMem, 'Ptr'), 'UTF-16')
	DllCall('GlobalUnlock', 'Ptr', hMem)
	DllCall('SetClipboardData', 'UInt', 13, 'Ptr', hMem)
	DllCall('CloseClipboard')
}
Hey FG and @teadrinker, thanks again for this code. I just want to check something, though. The manual says that "If Target is omitted, this function returns the required buffer size in bytes, including space for the null-terminator." Based on this, can we omit the ' * 2' in DllCall('GlobalAlloc', 'UInt', 0x42, 'Ptr', StrPut(Text, 'UTF-16') * 2, 'Ptr')?

teadrinker
Posts: 4325
Joined: 29 Mar 2015, 09:41
Contact:

Re: ComObject("HTMLfile") for extracting HTML Source from clipboard

Post by teadrinker » 03 Apr 2023, 14:02

Yep, in AHK v1 StrPut() returns the size in characters, not bytes, hence the confusion.

Post Reply

Return to “Ask for Help (v2)”