In my efforts to add Twitch badges to my richtextbox console using an RTF conversion method, I’ve managed to get every single badge except subscriber badges to download to memory, convert to byte[], convert byte[] to RTF image and embed into the richtextbox’s SelectedRTF.
The code I’m using to get the badges is as follows:
Assign all badge strings to the appropriate jsonData
using(var webClient = new WebClient()) {
var channelBadgesURL = string.Format("https://api.twitch.tv/kraken/chat/{0}/badges", WiggleBotSettings.Instance.Broadcaster);
var webData = webClient.DownloadString(channelBadgesURL);
var jsonData = JObject.Parse(webData);
BotClient.StaffBadge = jsonData["staff"]["image"].ToString();
BotClient.AdminBadge = jsonData["admin"]["image"].ToString();
BotClient.GlobalModBadge = jsonData["global_mod"]["image"].ToString();
BotClient.BroadcasterBadge = jsonData["broadcaster"]["image"].ToString();
BotClient.ModBadge = jsonData["mod"]["image"].ToString();
BotClient.SubscriberBadge = !jsonData["subscriber"].IsNullOrEmpty() ? jsonData["subscriber"]["image"].ToString() : "";
BotClient.TurboBadge = jsonData["turbo"]["image"].ToString();
}
Append the image at the given position in the RichTextBox:
WiggleBotVariables.wiggleBotForm.richtextboxConsole.AppendImage(BotClient.SubscriberBadge);
Download the data of the image, slap it in a MemoryStream, and apply it to an Image object
public static void AppendImage(this RichTextBox rtb, string url) {
var imgBytes = DownloadData(url);
using(var ms = new MemoryStream(imgBytes)) {
using(var img = Image.FromStream(ms)) {
rtb.SelectedRtf = GetEmbedImageString((Bitmap)img);
}
}
}
Get the byte[] of an image from a URL
private static byte[] DownloadData(string url) {
byte[] downloadedData;
//Get a data stream from the url
var req = WebRequest.Create(url);
var response = req.GetResponse();
using(var memStream = new MemoryStream()) {
using(var stream = response.GetResponseStream()) {
//Download in chunks
var buffer = new byte[1024];
//Download to memory
//Note: adjust the streams here to download directly to the hard drive
while(true) {
//Try to read the data
if(stream != null) {
var bytesRead = stream.Read(buffer, 0, buffer.Length);
if(bytesRead == 0) {
break;
}
//Write the downloaded data
memStream.Write(buffer, 0, bytesRead);
}
}
//Convert the downloaded stream to a byte array
downloadedData = memStream.ToArray();
}
}
return downloadedData;
}
Get the EmbedImageString that RTF understands in the RichTextBox
public static string GetEmbedImageString(Bitmap image) {
Metafile metafile;
float dpiX, dpiY;
using(var g = Graphics.FromImage(image)) {
var hDC = g.GetHdc();
metafile = new Metafile(hDC, EmfType.EmfOnly);
g.ReleaseHdc(hDC);
}
using(var g = Graphics.FromImage(metafile)) {
g.DrawImage(image, 0, 0);
dpiX = g.DpiX;
dpiY = g.DpiY;
}
var hEmf = metafile.GetHenhmetafile();
var bufferSize = GdipEmfToWmfBits(hEmf, 0, null, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
var buffer = new byte[bufferSize];
GdipEmfToWmfBits(hEmf, bufferSize, buffer, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
var hmf = SetMetaFileBitsEx(bufferSize, buffer);
var tempfile = Path.GetTempFileName();
CopyMetaFile(hmf, tempfile);
DeleteMetaFile(hmf);
DeleteEnhMetaFile(hEmf);
var stream = new MemoryStream();
var data = File.ReadAllBytes(tempfile);
var count = data.Length;
stream.Write(data, 0, count);
var sb = new StringBuilder();
sb.Append(@"{\rtf1");
sb.Append(@"{\pict");
sb.Append(@"\wmetafile8");
sb.Append(@"\picw" + (int)((image.Width / dpiX) * 2540));
sb.Append(@"\pich" + (int)((image.Height / dpiY) * 2540));
sb.Append(@"\picwgoal" + (int)((image.Width / dpiX) * 1440));
sb.Append(@"\pichgoal" + (int)((image.Height / dpiY) * 1440));
sb.Append(@" " + BitConverter.ToString(stream.ToArray()).Replace("-", ""));
sb.Append(@"}}");
return sb.ToString();
}
Necessary DllImports and flags for GetEmbedImageString to function
[Flags]
enum EmfToWmfBitsFlags {
EmfToWmfBitsFlagsDefault = 0x00000000,
EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
EmfToWmfBitsFlagsNoXORClip = 0x00000004
}
const int MM_ISOTROPIC = 7;
const int MM_ANISOTROPIC = 8;
[DllImport("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize, byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
[DllImport("gdi32.dll")]
private static extern IntPtr SetMetaFileBitsEx(uint _bufferSize, byte[] _buffer);
[DllImport("gdi32.dll")]
private static extern IntPtr CopyMetaFile(IntPtr hWmf, string filename);
[DllImport("gdi32.dll")]
private static extern bool DeleteMetaFile(IntPtr hWmf);
[DllImport("gdi32.dll")]
private static extern bool DeleteEnhMetaFile(IntPtr hEmf);
I believe I’ve provided everything needed to reproduce. If anyone has an idea for why subscriber badges aren’t properly displaying, that would be great!