Sage X3 convert RTF to plain text

Here a function to convert a Clob rtf text to plain text

Example of function that return the converted text into a Char variable
Good only for short text, less than 250 chars

Funprog F_RTF2TEXT(CLOB)
Variable Clbfile CLOB
  
    Local Integer LENTXMT : LENTXMT = 250
    Local Integer NRIGHE : NRIGHE = 4            # 4 = 250x4 char
 
    Local Char XOUT(LENTXMT)(NRIGHE) 
     
    Call S_RTF2TEXT(LENTXMT,CLOB,XOUT,NRIGHE) 
      
   #Concatenate the row with plain text, by removing the empty space
    Local Char XRET(250) : Raz XRET
    For XI = 0 To NRIGHE
       XRET += vireblc(XOUT(XI),2)  
    Next
 
End XRET

Example,
Convert the header text of a sales order

$MYTEST
    If !clalev ([F:SOH]) : Local File SORDER  [F:SOH] : Endif
    If !clalev ([F:TXC]) : Local File TEXCLOB [F:TXC] : Endif
    
    Filter [F:SOH]
    Filter [F:TXC]
    For [F:SOH] Where SOHNUM = "000000001"
        For [F:TXC] Where CODE = [F:SOH]SOHTEX1         
	    local char MYTEXT(250)
            MYTEXT = func F_RTF2TEXT([F:TXC]TEXTE)
            Infbox "MYTEXT= "+ MYTEXT
        Next
    Next
Return



Core procedures:

Subprog S_RTF2TEXT(PLENRIGAOUT,CLOB,PTXTOUT,PNRIGEOUT)
Value Integer PLENRIGAOUT
Variable Clbfile CLOB
Variable Char PTXTOUT
Variable Integer PNRIGEOUT

    Local Integer XRIGHE
    Local Integer XNOL
    Local Integer XNCH
    Local Char XCH(1)
    Local Char XCHPREV(1)
    Local Integer XSTRTAG1 : XSTRTAG1 = 0    
    Local Integer XSTRTAG2 : XSTRTAG2 = 0    

    Local Integer LENGHT : LENGHT = len(CLOB)
    Local Integer CLOTAI : CLOTAI = int(LENGHT/GCLOLNG)+1
    
    Local  Char    PRTFIN   (GCLOLNG)(CLOTAI)

    Setlob PRTFIN With CLOB



    XRIGHE = dim(PRTFIN,1)

    Local Char XOUT(250)(XRIGHE)
    Local Integer XOUTRLEN(XRIGHE)
    Local Char RIGA(250)
    Local Integer XESCAPE 



    #Toglie i tag RTF

    For XNOL = 0 To XRIGHE - 1
        Raz RIGA
        RIGA = PRTFIN(XNOL)
        XOUTRLEN(XNOL) = 0
        For XNCH = 1 To len(RIGA)
            XCHPREV = XCH            
            
            XCH = mid$(RIGA,XNCH,1) 
            If XCH = "\" Then 
                XESCAPE = -1
                If func F_GETNEXTCHR(PRTFIN,1,XNCH,XNOL,XRIGHE)= "}" Then
                    XCHPREV = XCH
                    XNCH = XNCH + 1
                    XCH = "}"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,1,XNCH,XNOL,XRIGHE)= "{" Then
                    XCHPREV = XCH
                    XNCH = XNCH + 1
                    XCH = "{"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,4,XNCH,XNOL,XRIGHE)= "pard" Then
                    XNCH = XNCH + 4
                    XCH = " "
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "par" Then
                    XNCH = XNCH + 3
                    XCH = " "
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'e0" Then
                    XNCH = XNCH + 3
                    XCH = "a"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'e9" Then
                    XNCH = XNCH + 3
                    XCH = "e"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'e8" Then
                    XNCH = XNCH + 3
                    XCH = "e"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'b0" Then
                    XNCH = XNCH + 3
                    XCH = "°"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'ec" Then
                    XNCH = XNCH + 3
                    XCH = "i"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'f2" Then
                    XNCH = XNCH + 3
                    XCH = "o"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "'f9" Then
                    XNCH = XNCH + 3
                    XCH = "u"
                    XESCAPE = 1
                Endif
                If func F_GETNEXTCHR(PRTFIN,2,XNCH,XNOL,XRIGHE)= "fs" Then #Font size
                    XNCH = XNCH + 2
                    XCH = ""
                    XESCAPE = -1
                Endif

                If XESCAPE = -1 Then
                        XSTRTAG1 = 1 
                        XNCH = XNCH + 1                         
                Endif
            Endif

            If XCH = "{" and XCHPREV <> "\" Then XSTRTAG2 = XSTRTAG2 + 1 : Endif

            If XSTRTAG1 = 0 and XSTRTAG2 <= 1  Then
                XOUT(XNOL) += XCH    
                XOUTRLEN(XNOL) += 1
            Endif  
            If XCH = " " or find(func F_GETNEXTCHR(PRTFIN,2,XNCH-1,XNOL,XRIGHE) ,'\}','\{','\\') > 0 Then XSTRTAG1 = 0 : Endif
            If XCH = "}" and XCHPREV <> "\" Then XSTRTAG2 = XSTRTAG2 - 1 : Endif
        Next             
    Next

    
    Local Integer XNOL2
    Local Integer XCONTA2

    XNOL2 = 0
    XCONTA2 = PLENRIGAOUT
    For XNOL = 0 To XRIGHE - 1 
        Raz RIGA
        RIGA = XOUT(XNOL)
        If XNOL = XRIGHE : RIGA = vireblc(RIGA,1) : Endif
        If len(RIGA) > 0 Then
            For XNCH = 1 To len(RIGA)
                XCH = mid$(RIGA,XNCH,1) 
                If XCH = chr$(13) Then
                    XCH = " "
                    If func F_GETNEXTCHR(XOUT,1,XNCH,XNOL,XRIGHE)= chr$(10) Then                    
                        XNCH = XNCH + 1
                    Endif
                Endif
                If XCH = chr$(10) Then
                    XCH = " "
                    If func F_GETNEXTCHR(XOUT,1,XNCH,XNOL,XRIGHE)= chr$(13) Then                    
                        XNCH = XNCH + 1
                    Endif
                Endif
                If XNCH = 1 and XNOL = 0 and XCH = "{" Then XCH = "" : Endif
                PNRIGEOUT = XNOL2 + 1
                PTXTOUT(XNOL2) += XCH
                XCONTA2 = XCONTA2 - 1
                If XCONTA2 = 0 Then
                    XNOL2 = XNOL2 + 1
                    XCONTA2 = PLENRIGAOUT
                Endif
            Next    
        Endif
    Next

    #tolgo la graffa finale 
    RIGA = vireblc(PTXTOUT(XNOL2),1)
    If mid$(RIGA,len(RIGA),1) = "}" Then
        PTXTOUT(XNOL2) =  mid$(RIGA,1,len(RIGA)-1)
    Endif


End 

############################
Funprog F_GETNEXTCHR(PARRIN,NCHNEXT,NCURPOS,NCURNOL,NTOTRIGHE)
Value Char PARRIN
Value Integer NCHNEXT
Value Integer NCURPOS
Value Integer NCURNOL
Value Integer NTOTRIGHE

    Local Char XRET(NCHNEXT) : XRET = ""

    Local Integer XNCH 
    Local Char RIGA(250)
    Local Integer XCONTA
    
    For XNOL = NCURNOL To NTOTRIGHE - 1
        RIGA = PARRIN(NCURNOL)
        For XNCH = NCURPOS + 1 To len(RIGA) - 1
            XRET += mid$(RIGA,XNCH,1)            
            XCONTA += 1
            If XCONTA = NCHNEXT Then
                Break
            Endif
        Next
        If XCONTA = NCHNEXT Then
            Break
        Endif
        NCURPOS = 0
    Next    
End XRET



Donwload the source and example file:

http://ma-tica.it/wordpress/RTF2TEXT.txt

Advertisements

32 thoughts on “Sage X3 convert RTF to plain text

  1. DOS

    merci infiniment pour cette fonction qui vient de me faciliter la tâche pour un export déjà assez complexe!!!!

    merci encore une fois

  2. MAD

    Hello,
    I just set up the function and it works fine, but one concern, the exported fields is truncated and I can not go beyond 99 character output. there’s there a way to export all the text if it’s 250 characters.

  3. matteo72

    Hi MAD,

    In the function F_RTF2TEXT, XOUT is an array, can you please check if the rest of the plain text is into the XOUT(1) (or next) ?

    Matteo

      1. matteo72

        Please try to use this new version of function F_RTF2TEXT.

        Funprog F_RTF2TEXT(CLOB)
        Variable Clbfile CLOB
         
            Local Integer LENTXMT : LENTXMT = 250
            Local Integer NRIGHE : NRIGHE = 4            # 4 = 250x4 char
        
            Local Char XOUT(LENTXMT)(NRIGHE) 
            
            Call S_RTF2TEXT(LENTXMT,CLOB,XOUT,NRIGHE) 
             
           #Concatenate the row with plain text, by removing the empty space
            Local Char XRET(250) : Raz XRET
            For XI = 0 To NRIGHE
               XRET += vireblc(XOUT(XI),2)  
            Next
        
        End XRET
        

        Matteo

  4. MAD

    thank you again for your return,

    I just tested the new version of your function, and it gives the same field at the output is truncated at the 99th character. you do not think that this is due to the fact that I export with a model and I use the GIMP variable (92). below my call your function:

    Local Clbfile TEXT (2)

    If dim ([F: SID] SIDTEX)> 0 and [F: SID] SIDTEX “”
    Read [TXC] TXC0 = [F: SID] SIDTEX
    [L] = func F_RTF2TEXT TEXT ([F: TXC] TEXT)
    GIMP (91) = toupper ([L] TEXT)
    endif

  5. MAD

    this is correct syntax for my call of your function:
    Local Clbfile TEXTE(2)

    If dim([F:SID]SIDTEX)>0 & [F:SID]SIDTEX””
    Read [TXC]TXC0=[F:SID]SIDTEX
    [L]TEXTE= func F_RTF2TEXT([F:TXC]TEXTE)
    GIMP(91)= toupper([L]TEXTE)
    Endif

    1. matteo72

      Hi,

      yes, I think you are right, the GIMP’s dimension is 99 characters.
      You could try to export by using GIMP(91) and GIMP(92):
      GIMP(91)= toupper(left$([L]TEXTE,99))
      GIMP(92)= toupper(mid$([L]TEXTE,100,99))

      Matteo

      1. MAD

        Hi,
        I just tested this and it works, sometimes to export my result but with a field separator; So the text is exported with a break .I think I’ll ask users they are limited to 99 character so as not to exceed.

        Example: see end

        REGULARISATION SUR FAC1505-0035791 DU 06/05/2015 ET SUR FAC1504-0033687 DU 29/04/2015 ERREUR SUR P;RIX

  6. Mike

    Hi,

    I’m trying to use your function in X3V7 and I am getting a compile error on the code. The error says it’s on line 116, column 78, Unknown Instruction: Illegal Character. I have not made any modifications to the code.

    This is the line causing the error:

    If XCH = “{” and XCHPREV <> “\” Then XSTRTAG2 = XSTRTAG2 + 1 : Endif

    1. matteo72

      Hi Mike,

      perhaps there are some bad hidden chars into the string. Sometime this happens when we copy and paste text in the X3 editor.

      Try to remove the line entirely and rewrite it manually directly inside the X3 editor.

      Are you using eclipse or the X3 editor (Script editor) ?

      Matteo

  7. Mike

    Hi Matteo,

    Ok, it working – almost. What I’m trying for a test is to read/convert the Header Text on a Sales Order. The Header text is actually “Header Text on SO00056”. Below is the little bit of code I put before your function (and by the way, I did replace the function F_RTF2TEXT code with the new version you posted later). When I run the code, it does convert and display the converted text, but then I’m getting another error: “CLOB : Variable Non-existent” on line “: Call S_RTF2TEXT(LENTXMT,CLOB,XOUT,NRIGHE). Here is my test code:

    If !clalev ([F:SOH]) : Local File SORDER[SOH] : Endif
    If !clalev ([F:TXC]) : Local File TEXCLOB[TXC] : Endif

    For [SOH] Where SOHNUM = “SO00056”
    For [TXC] Where CODE = [SOH]SOHTEX1
    Infbox “Sold To = “+[SOH]BPCORD
    Infbox “TEXTE= “+[TXC]TEXTE
    MYTEXT = FUNC F_RTF2TEXT([SOH]SOHTEX1)
    Infbox “MYTEXT= “+MYTEXT
    Next
    Next

    1. matteo72

      Hi Mike,

      try to put [TXC]TEXTE instead of [SOH]SOHTEX1 as parameter of the F_RTF2TEXT function.
      [SOH]SOHTEX1 is a char field containing the TEXCLOB’s key in which the rtf text is stored.
      [TXC]TEXTE is the TEXCLOB’s field that contains the rtf

      Matteo

      1. Mike

        Hi Matteo,

        I actually did catch that and corrected it, and I get the converted text back in my Infbox, then still get the error mentioned before abut the CLOB field.

        This is the current code:
        If !clalev ([F:SOH]) : Local File SORDER[SOH] : Endif
        If !clalev ([F:TXC]) : Local File TEXCLOB[TXC] : Endif

        For [SOH] Where SOHNUM = “SO00056”
        For [TXC] Where CODE = [SOH]SOHTEX1
        Infbox “Sold To = “+[SOH]BPCORD
        Infbox “TEXTE= “+[TXC]TEXTE
        MYTEXT = FUNC F_RTF2TEXT([TXC]TEXTE)
        Infbox “MYTEXT= “+MYTEXT
        Next
        Next

        I get this result: MYTEXT= Header Text on SO00056

        but after clicking Ok on the message box, I still get this error:
        CLOB : Variable Non-existent
        @PILOT.TRT/MIKETEST8$adx(22) : Call S_RTF2TEXT(LENTXMT,CLOB,XOUT,NRIGHE)

  8. matteo72

    Hi Mike,

    in the most inner For loop statement, you have to test if the [TXC]TEXTE is not empty before to pass it to F_RTF2TEXT function, I think.

    try this:
    If vireblc([TXC]TEXTE,2) “” Then
    MYTEXT = Func F_RTF2TEXT([TXC]TEXTE)
    Infbox “MYTEXT= “+MYTEXT
    EndIf

    Matteo

  9. Mike

    Hi Matteo,

    I still get the same errors. It doesn’t make sense to me that it says CLOB variable non-existent, because it obviously works since at one point it displays the text??

    This is my test code now:

    For [SOH] Where SOHNUM = “SO00056”
    For [TXC] Where CODE = [SOH]SOHTEX1
    Infbox “Sold To = “+[SOH]BPCORD
    If vireblc([TXC]TEXTE,2) “”
    MYTEXT = FUNC F_RTF2TEXT([TXC]TEXTE)
    Infbox “MYTEXT= “+MYTEXT
    Endif
    Next
    Next

    1. Mike

      Hi Matteo,

      I think I figured this out – a stupid mistake of course. In my little test code, I didn’t have an End at the end of my code, so after it ran the first time, it was dropping back down into the function again.

      Thank you very much for you’re help! This is a great tool!

      Mike

  10. Mike

    Hi again Matteo,

    Is there a way to handle RTF text that is multiple lines? For example, in the SO Footer Text I have the following text:

    This is the Footer note for SO00061.

    SO00061 line 3
    SO00061 line 4
    SO00061 line 5

    After running this through the function, it is converted to this:

    This is the Footer note for SO00061. parSO00061 line 3 fs22 SO00061 line 4 fs22 SO00061 line 5

    1. Mike

      Here is the actual CLOB:

      {\rtf1\ansi\uc0\pard{These are the Header notes for SO00061 Line 1.\par}\par This is the second line of the notes for SO00061.}

      1. Mike

        …sorry wrong line. This is the correct line:

        {\rtf1\ansi\uc0\pard This is the Footer note for SO00061.\par\par\fs22 SO00061 line 3\par\fs22 SO00061 line 4\par\fs22 SO00061 line 5\par}

  11. matteo72

    Hi Mike,

    please consider that my procedure doesn’t cover all the RTF specification.
    In your case it wasn’t managed the rtf tag “\fs” which define the font size.
    I have added it in the new version of the procedure.
    I have also added a link to download the plain text of the procedures, at the end of the article, that is better than do the copy and paste from wordpress

    Matteo

    1. Mike

      Hi Matteo,

      Thank your for taking the time to update this. This is a great tool to have!

      I’m still getting a “par” in my out put as seen below. Below are two examples because the results were different. In the first note, the first “par” was removed, but the second remained. In the second example, the the first remained and the second was removed. Looks like the issue might be when there is a “\par\par” when there is a blank line between two lines.

      -Mike

      Example 1:

      These are notes on SO00013.

      This is line 2 of the notes.
      This is line 4.

      Result:

      These are notes on SO00013. This is line 2 of the notes. par This is line 4

      Actual RTF text:

      {\rtf1\ansi\uc0\pard These are notes on SO00013.\par This is line 2 of the notes.\par\par This is line 4.}

      Example 2:

      These are the So Header Text notes for SO00017. Only the first 255 characters of these notes will be displayed in this field.

      This is line 3.
      This is line 4 which is the last line of the note.

      Result:

      These are the So Header Text notes for SO00017. Only the first 255 characters of these notes will be displayed in this field. par This is line 3. This is line 4 which is the last line of the note.

      Actual RTF text:
      {\rtf1\ansi\uc0\pard These are the So Header Text notes for SO00017. Only the first 255 characters of these notes will be displayed in this field.\par\par This is line 3.\par This is line 4 which is the last line of the note.}

      1. matteo72

        Hi Mike,
        thank you too for your patience, your remarks get me to discover some bugs in this procedure.

        There was an error in this statements

        If func F_GETNEXTCHR(PRTFIN,3,XNCH,XNOL,XRIGHE)= "par" Then
                            XNCH = XNCH + 4
                            XCH = " "
                            XESCAPE = 1
                        Endif
        

        the wrong sentence was XNCH = XNCH + 4, I have changed it with XNCH = XNCH + 3

        Try now, thanks

        Matteo

  12. Mike

    Hi Matteo,

    All the thanks go to you for taking the time to help others like this!

    I tried the change, and the “par” are gone, but now the lines are starting with a “d” for some reason:

    Example 1:

    d These are the So Header Text notes for SO00017. Only the first 255 characters of these notes will be displayed in this field. This is line 3. This is line 4 which is the last line of the note.

    Example 2:

    d These are notes on SO00013. This is line 2 of the notes. This is line 4.

    By the way, if perhaps this helps anyone else, my goal is to display all notes that may have been entered on a SO in a single field. These notes can be entered in the Header Text and Footer Text. So, to combine these, I use this code together with your great function. In the example below, this is used on a screen with a grid/table listing SO’s. I added an Before-Entry field action on the NBLIG to call this code. Then, as the user clicks on each line, all the SO notes will be displayed in the All SO Notes field below the grid.

    – Mike

    ############################################################################
    Local char ALLSONOTES(255)

    If !clalev ([F:SOH]) : Local File SORDER[SOH] : Endif
    If !clalev ([F:TXC]) : Local File TEXCLOB[TXC] : Endif

    ALLSONOTES = “Header Text:”
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    READ [SOH]SOH0 = [M:YCRP]SONUM(nolign-1)
    READ [TXC]TXC0 = [SOH]SOHTEX1
    If [SOH]SOHTEX1 “”
    ALLSONOTES += FUNC F_RTF2TEXT([TXC]TEXTE)
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    Endif

    ALLSONOTES += “Footer Text:”
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    ALLSONOTES += ” “+chr$(13)+chr$(10)
    READ [TXC]TXC0 = [SOH]SOHTEX2
    If [SOH]SOHTEX2 “”
    ALLSONOTES += FUNC F_RTF2TEXT([TXC]TEXTE)+chr$(13)+chr$(10)
    Endif

    [M:YCRP]SONOTES = ALLSONOTES
    Affzo

    End

    ########################################################################

    1. Mike

      in the code above, the less than greater than symbols (Not Equal) are missing from lines
      If [SOH]SOHTEX1 “” . I guess they get dropped by the editor? It should be

      If [SOH]SOHTEX1 not equal sign goes here “”

      -Mike

      1. matteo72

        Yes, the editor sometimes remove this kind of chars.
        That’s why I decide to add a link to download the source in plain text.

        Matteo

    2. matteo72

      Hi Mike,

      The ‘d’ was caused by my last change. Now I added this sentence:

      If func F_GETNEXTCHR(PRTFIN,4,XNCH,XNOL,XRIGHE)= “pard” Then
      XNCH = XNCH + 4
      XCH = ” ”
      XESCAPE = 1
      Endif

      Matteo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s