• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2549
  • Last Modified:

Extracting an image from a richtextbox, Part 2

Okay, new snag.

This is related to a previous question:

The previous question was answered with a good solution, but I have run into one problem with the code.  I need to not use the Clipboard.  Here's a code sample:

Dim n As Integer
        Dim text As String

        For n = 0 To RichTextBox1.TextLength

            'select piece by piece
            RichTextBox1.Select(n, 1)  

            'Check is this an Object
            If RichTextBox1.SelectionType = RichTextBoxSelectionTypes.Object Then
                'When found, copy this to Clipboard!
            End If

Is there a way to extract the object straight from the richtextbox without using the clipboard to copy?

Thanks for any help you can give.
  • 9
  • 7
  • 2
1 Solution
AntharAuthor Commented:
There are some problems with that solution.  The first being that it uses the clipboard.  

I think I should explain what I'm doing in a little more detail.

1. The end user is looking at an ASP.NET page, from this page they upload an RTF file.  
2. I then take that file and pass the rtf code to a web service.
3. The web service loads a richtextbox into memory and sets the rtf property to the rtf that the end user uploaded
4. The web service loops through the contents of the richtextbox looking for images

All of that works fine.

The problem arises when the web service finds an image.  The code snippet I listed above might be fine if this were a client side app, but I can't use the clipboard on my web service server because conflicts will happen when more than one file is being processed and both will need access to the clipboard for their images.  The fact that the processing occurs on the server is also why any solution that involves capturing the image to a picturebox on the client side won't work.

Any ideas?
Ok heres something else that is interesting......

[Webinar] Kill tickets & tabs using PowerShell

Are you tired of cycling through the same browser tabs everyday to close the same repetitive tickets? In this webinar JumpCloud will show how you can leverage RESTful APIs to build your own PowerShell modules to kill tickets & tabs using the PowerShell command Invoke-RestMethod.

AntharAuthor Commented:
Thanks for trying again.  If I could get the source code for that it would probably be helpful.  Unfortunately, it scans for images in files on the hard drive, I didn't see any mention of a programming interface.  I need to avoid saving the files to the hard drive, because we don't want to deal with write permissions.  Both the images and the text of the file are being prepped for database storage.

Hello Anthar...  here am I again.. I am sorry my solution the other day did not help you... I will give it (again) a try ;-)

I have the source code still on my harddisk... I am looking forward to send you (again) a solution...
AntharAuthor Commented:
Thanks, Daniël.  I was hoping you might see this and have some idea.  Don't get me wrong, the solution you gave me before was very helpful, I just hadn't thought through the clipboard issue.

Anthar,... again i've been trying real hard to get it working. I tried already much (like creating a own made clipboard, with IBandObject variable etc.., no succes).

But I just got this idea: As executing the Copy&Paste (to/from Clipboard) on the serverside causes problems you might want to think about code (javascripts?) that executes on clientside. I don't (yet) know what is possible.. but you might think about this...

The other idea is to work with threading (I have no experience on this subject on windows.form or asp.net) on serverside,... you put all the files in a line to be processed... And do scan then one after the other...

After all,.. this is more complicated than you would think :-)
AntharAuthor Commented:
I appreciate your help, Daniël.  

A client side copy and paste isn't an option because there is no client side.  They are submitting an already formatted and saved rtf file.  

As far as queueing the clipboard operations go, I suppose I could give that a try, but then there are a few other issues.  Namely, I've been having trouble accessing the clipboard from the WebService.  It doesn't error on the Copy method, but when I try to actually get the data it says there's nothing there.  That's not the main reason I'm trying to get around the clipboard, but it is certainly a consideration.

Indeed,... I realized later that the file is being uploaded...

This brings me to another idea, this should be a nice try:

Download the Office XP PIA: http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21100155.html#11846630

> Include the interop.word.dll into your project
    (I don't know if this works on the server... You might need to ask you ISP/hosting Company/ or yourself ;-)  to install this .dll in the MMC)

> Then define a new application and document and open this uploaded RTF file,

   code should look something like this:

        Dim wordApp As Microsoft.Office.Interop.Word.Application = New Microsoft.Office.Interop.Word.Application
        Dim doc As Microsoft.Office.Interop.Word.Document = wordApp.Documents.Open(Path_to_rtf_file, True)

> then you will be able to save this with code like this.

        doc.SaveAs(Your_html_file, Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatHTML)


This might sound scary.. but try it yourself.. Open an example RTF into Word (I used office XP) and Save this file again as webpage... The Images from the rtf should be in the folder:


And you know what's nice about this?... the folder contains standard a file "files.xml" in wich you can (of course) find the names of the files with images...

For now I cannot give you a better solution... This is my best bet at this moment ;-)

Here I have working code for a windows Form app (Form with 1 Button)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim BestandsNaam As String
        Dim wordApp As New Microsoft.Office.Interop.Word.Application
        Dim doc As New Microsoft.Office.Interop.Word.Document

        If LoadParameterFile.ShowDialog() = DialogResult.OK Then
            BestandsNaam = LoadParameterFile.FileName
            doc = wordApp.Documents.Open(BestandsNaam, False)
        End If

        If SaveParameterFile.ShowDialog() = DialogResult.OK Then
            BestandsNaam = SaveParameterFile.FileName
            doc.SaveAs(BestandsNaam, Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatHTML)
        End If


    End Sub

As posted earlier (but I made a mistake in the hyperlink).. to be able to run this you will need the proper Reference (Microsoft.Office.Interop.Word.dll). Download from:



> I know (if this works on an ASP.Net Server) you have to deal with write permissions. But that should not be the biggest problem since you also must need a location to store your uploaded RTF file...

> If I am right... you are able to do more settings on the saving procedure. e.g. you can force the Word-dll to use PNG....
AntharAuthor Commented:
Actually, I'm not saving the file directly to the harddrive.  I'm processing the file in memory and then inserting the text and images into seperate database fields.  I'll do it as a last resort, but I'd like to avoid writing files to the harddrive if at all possible, in part because we use a load balanced server cluster for a web sites, having to write to a common share (while not impossible) would complicatre things.  Using Word seems like a good idea, and I looked into it, but if I use that method, there's no way to get around writing to the hard drive.  I'd really like to find a way to get the images directly from the rtf.
Okay.. I'll keep trying...
holy beep... I just did it! Freaking amazing... (I am going crazy right now). I just suceed to save this picture from a richtextbox... I was first reversing some c# code that made it possible to input images in Richtextbox... But I had to get MSDN for other functions

I have to work out some more things, like risizing etc... I see now my test pic a little big... I also have to test all 8 modes the Windows Metafile can be stored in by the RTF-program our users use...

Technical: I used the image data (rtfsource) for the bitmap, cut the hexadecimal code off and convert it into a binary buffer, then I read it with some functions from GDI32.dll... save it to metafile format and show it onto the picturebox.

I cant tell how much more time..

'kay here it is...:

This worked for me. You must first test it with different RTF files with different images... I have tried to make it so COMPLETE as possible.. but I might post some code-updates from now if I find errors or so.

Make a windows.form with:

> button 'button1'  (named "Capture")
> button 'button2'  (named "Open")
> NumericUpDown 'NumericUpDown1'
> Picturebox 'pb'
> RichTextBox 'RichTextBox1'
> Textbox 'TextBox2'

and put this code into you project (Form1.vb; I leave out the windows form generated code):

Imports System.Drawing.Imaging
Imports System.IO

Public Class Form1
    Inherits System.Windows.Forms.Form

'' ---- cut   WINDOWS GENERATED CODE  cut -----

    Private Declare Function SetEnhMetaFileBits Lib "gdi32" (ByVal cbBuffer As Integer, ByRef lpData As Byte) As IntPtr
    Private Declare Function SetWinMetaFileBits Lib "gdi32" (ByVal cbBuffer As Integer, ByRef lpbBuffer As Byte, ByVal hDCRef As IntPtr, ByRef lpmfp As METAFILEPICT) As IntPtr

    Private Structure METAFILEPICT
        Dim mm As Integer
        Dim xExt As Integer
        Dim yExt As Integer
        Dim hMF As Integer
    End Structure

    Dim HexString As String
    Dim Buffer(1) As Byte
    Dim Pictures() As Metafile

    'User Events -----------------------
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    End Sub
    Private Sub NumericUpDown1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NumericUpDown1.ValueChanged
        If Pictures.Length > 0 Then
            pb.Image = Pictures(NumericUpDown1.Value)
        End If
    End Sub
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    End Sub

    'Subs / Functions -------------------
    Sub OpenFile()
        Dim FileName, tekst As String
        If LoadParameterFile.ShowDialog() = DialogResult.OK Then
            FileName = LoadParameterFile.FileName
            Dim fs As New FileStream(FileName, FileMode.Open, FileAccess.Read)
            Dim d As New StreamReader(fs)
            d.BaseStream.Seek(0, SeekOrigin.Begin)
            RichTextBox1.Rtf = d.ReadToEnd
            TextBox2.Text = RichTextBox1.Rtf

            Dim a As RichTextBoxSelectionTypes

        End If 'DialogOk_If
    End Sub

    Private Sub FindMetafiles()
        Dim plaatje, BoxCopy, Properties As String
        Dim i, n, m, StartCode, StartofImage, EndofImage As Long
        Dim FoundBegin, FoundEnd As Boolean
        Dim number As Int32 = 0
        Dim xnorm, ynorm, xgoal, ygoal, mmtype As Integer

        FoundBegin = False
        FoundEnd = False

        'Find the Pics and save them to Metafile array:

        BoxCopy = RichTextBox1.Rtf

        For n = 0 To BoxCopy.Length - 7
            If BoxCopy.Substring(n, 6) = "{\pict" Then
                StartofImage = n
                FoundBegin = True
            End If
            If FoundBegin = True Then
                If BoxCopy.Substring(n, 1) = "}" Then
                    EndofImage = n - 2
                    'n = BoxCopy.Length
                    FoundEnd = True
                End If
            End If
            'Execute the piece if the beginning({\pic) and the end (}) of the image is found
            If FoundBegin = True And FoundEnd = True Then
                FoundEnd = False
                FoundBegin = False
                number += 1
                ReDim Preserve Pictures(number)
                'Find beginning of Hex code of picture; this might give problems if the image properties are more than one line!!
                StartCode = StartofImage
                For m = StartofImage To StartofImage + 200 'max(?) of preferences of picture (e.g. \pichgoal)
                    If BoxCopy.Substring(m, 1).GetHashCode = 177583 Then
                        StartCode = m + 1
                        m = StartofImage + 200
                    End If
                'We now can cut off the hexcode to process it further
                HexString = BoxCopy.Substring(StartCode, EndofImage - StartCode)
                'We also need the properties of the image (\picxgoal...etc) so:
                Properties = BoxCopy.Substring(StartofImage, StartCode - StartofImage) & "             "  'Add this extra spaces (>=10) as 'safety' zone so the
                'Now try to extract the image sizes and mmtype:
                For m = 0 To Properties.Length - 10 'This -10 is for preventing error when string is at end and the program checks for "\wmetafile"
                    If Properties.Substring(m, 10) = "\wmetafile" Then
                        mmtype = Val(Properties.Substring(m + 10, 1))
                        'MsgBox("mmtype: " & mmtype)

                    ElseIf Properties.Substring(m, 5) = "\picw" And Not Properties.Substring(m, 9) = "\picwgoal" Then
                        xnorm = Val(Properties.Substring(m + 5, 4))     'Warning: I assume here that the number is 4 digits long (xxxx),.. this goes wrong with 1,2,3,,5,6.. digits!!
                        'MsgBox("xnorm: " & xnorm)

                    ElseIf Properties.Substring(m, 5) = "\pich" And Not Properties.Substring(m, 9) = "\pichgoal" Then
                        ynorm = Val(Properties.Substring(m + 5, 4))     'Warning: I assume here that the number is 4 digits long (xxxx),.. this goes wrong with 1,2,3,,5,6.. digits!
                        'MsgBox("ynorm: " & ynorm)

                    ElseIf Properties.Substring(m, 9) = "\picwgoal" Then
                        xgoal = Val(Properties.Substring(m + 9, 4))     'Warning: I assume here that the number is 4 digits long (xxxx),.. this goes wrong with 1,2,3,,5,6.. digits!
                        'MsgBox("xgoal: " & xgoal)

                    ElseIf Properties.Substring(m, 9) = "\pichgoal" Then
                        ygoal = Val(Properties.Substring(m + 9, 4))     'Warning: I assume here that the number is 4 digits long (xxxx),.. this goes wrong with 1,2,3,,5,6.. digits!
                        'MsgBox("ygoal: " & ygoal)
                    End If
                Pictures(number) = paint_it(Buffer, mmtype, xgoal, ygoal)
            End If

        MsgBox("Number of pictures found: " & number)
        NumericUpDown1.Maximum = number
        NumericUpDown1.Minimum = 1
    End Sub

    Sub RemoveCrLr()
        HexString = HexString.Replace(Constants.vbCrLf, "")
    End Sub

    Sub CopyIntoBuffer()
        Dim i As Integer
        Dim intArraySize As Integer = CInt((Len(HexString) + 1) / 2) - 1
        ReDim Buffer(intArraySize)
        For i = 0 To intArraySize
            Buffer(i) = "&H" & Mid(HexString, i * 2 + 1, 2)
        Next i
    End Sub

    Function paint_it(ByVal bufferimage() As Byte, _
                      ByVal mtype As Integer, _
                      ByVal p_x As Integer, _
                      ByVal p_y As Integer) As Metafile

        Dim lengte As Integer = bufferimage.Length
        Dim pointer As IntPtr

        Dim _metaFile As METAFILEPICT

        _metaFile.mm = mtype
        _metaFile.xExt = p_x
        _metaFile.yExt = p_y

        Dim _hEmf As IntPtr

        pointer = SetWinMetaFileBits(lengte, bufferimage(0), _hEmf, _metaFile)

        paint_it = New Imaging.Metafile(pointer, True)
    End Function

End Class
AntharAuthor Commented:
That's so great, I can't believe you did it!  Wow, it doesn't even look that bad, using this code I might not even have to load the richtextbox into memory.  Thank you so much, Daniël.  I'm going to plug it in to my Web Service, I'll let you know how it goes.  Thanks, again.
I couldnt believe it either... I was testing it, and suddenly the program printed the image HUGE on my picturebox... (I was dancing around ;-) ). You sure can imagine after 3 days of trail & error....

But I wanted to add this: I couldn't test the code throughly since its such a big project; but the following assumptions does the code make:

> The picture is always (!) stored as wmetafile type (windows metafile) into the rtf. If not, this pic is not recognised... ( but all the pics I tried went well!)
    If this is a problem (I don't expect it to be), we have to work all other formats out..... (I don't want that!)
> The picture's dimensions are >=1000 and <= 9999. This can be adjusted, but most pics will fall into that range.

And further; the code loops through all RTF and puts all images found (& recognised) into a metafile-array. I added and NumericUpDown to my form to check them all. You might first wanna create your own windows.Forms version so you can see how it works. At least it doesn't use clipboard but an private string and byte-array to store those ones and zeros...

Sweet dreams, Daniël
AntharAuthor Commented:
Man, you are a life saver.  That code worked beautifully, in both the form I created to test, and in the Web Service I have to use for the app.

I'm pretty sure that the images will always be stored as metafiles, so that shouldn't be a problem.

I did fix the 1000 to 9999 assumption, though, just to be safe.  I thought you might like to see the code, nothing too complicated:

Dim sXnorm As String  'Declare at the beginning of the function
Dim iNextNumber As Integer
Dim x

'< Code Omitted >

ElseIf Properties.Substring(m, 5) = "\picw" And Not Properties.Substring(m, 9) = "\picwgoal" Then
  'Warning: I assume here that the number is 4 digits long (xxxx),.. this goes wrong with 1,2,3,,5,6.. digits!!
  'xnorm = Val(Properties.Substring(m + 5, 4))    
  'The code below fixes the 4 digit assumption
  iNextNumber = 5
  'Initialize the string...I had done this on declaration at the start of the function, but I quickly found that it had to be initialized in the loop if there is more than one image
  sXnorm = ""
  x = Properties.Substring(m + iNextNumber, 1)
  Do While IsNumeric(x)
    sXnorm = sXnorm & Properties.Substring(m + iNextNumber, 1)
    iNextNumber += 1
    x = Properties.Substring(m + iNextNumber, 1)
  xnorm = Val(sXnorm)

Just repeat for each of the 4 xnorm, ynorm, xgoal, ygoal.

Like I said, nothing special.

Thanks for all your help Daniël, you've been a HUGE help.
Nice! And I want to tremendously thank you for the feedback,... Thats always something pleasent to read back ;-)

I knew this extra code wouldn't be very difficult, but I had already written the code for multiple recognision and storage to metafile-array, so I left it to you. Sounds lazy, but now you altered the code, I am convinced you gave it a good look ;-) (Just kiddin'...) Now it is really complete! Thanks!

I might one day need this myself for my own website... its really a easy way (with asp.net) to store uploaded text with pics...

Good luck so far needed with you project, maybe we meet again on a "Extracting an image from a richtextbox, Part 3" ... :~)

Daniël Trommel

Featured Post

Receive 1:1 tech help

Solve your biggest tech problems alongside global tech experts with 1:1 help.

  • 9
  • 7
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now