Skip to Content
Technical Articles

Converting / Printing Subscript and Superscript (from Your Data) in Crystal Reports

You may have a database field which contains code containing characters that you would like to convert to / print as subscript or superscript. One workaround has been to just paste the superscript or subscript character on top of an existing text object. This may be fine for static layouts but doesn’t work at all for changing data.

Below is a formula which will convert the tagged values in your database to proper subscript or superscript in your output should they match up with the values listed.

This assumes that you are using a font which can actually handle those characters. DejaVu Sans is an example of a font that will print those unicode subranges properly. Please note the characters in the formula (look at the sections with “replace”) that will be converted for both subscript and superscript.

Note: This post has been edited to include many more characters than before. Also note that there are alpha characters (mostly in subscript) that will not be converted as there is no match in this font.

To use this formula for your data, change the s stringvar to your field and then comment out the “s:=” assignment below.

If needed, change the next four variables which represent the separators for your values which should be in subscript or superscript. E.g. H<sub>2</sub>O where “2” should be subscripted.  Please see farther below for an example input and out

////////////
//
// subscript superscript
//
////////////

// use this formula to convert subscript and superscript tagged values in your database text
// a font that supports all of the values below must be used...e.g. DejaVu Sans

////
// formula settings
////

stringvar s; // set this to your field and comment out the "s" assignment below

s:= 'The human body is up to 75% H<sub>2</sub>O. O<sub>2</sub> accounts for about 20% of our air. Eight is 2<sup>3</sup>. This is the N<sub>th</sub> time. That was the 3<sup>rd</sup>.';
//s:= 'start sub <sub>0123456789+-=()</sub> endsub. start sup <sup>0123456789+</sup> end sup.';

// set these next four values to the separators used in your code

stringvar subS:= '<sub>'; // beginning of a subscript
stringvar subE:= '</sub>'; // end of a subscript
stringvar supS:= '<sup>'; // beginning of a superscript
stringvar supE:= '</sup>'; // end of a superscript

////
// the syntax below may need to be changed depending on the characters in your database
////

// this part deals with subscript
stringvar array sa:= split(s, subS);
while numbervar i < ubound(sa) do (
    i:= i + 1;
    stringvar t:= sa[i];
    if instr(t, subE) > 0 then (
        stringvar t1:= split(t,subE)[1];
        // note no sub A (upper case) yet
        t1:= replace(t1, 'A', 'ₐ'); t1:= replace(t1, '0041', 'ₐ');
        t1:= replace(t1, 'a', 'ₐ'); t1:= replace(t1, '0061', 'ₐ'); t1:= replace(t1, '2090', 'ₐ');
        // note no sub E (upper case) yet
        t1:= replace(t1, 'E', 'ₑ'); t1:= replace(t1, '0045', 'ₑ');
        t1:= replace(t1, 'e', 'ₑ'); t1:= replace(t1, '0065', 'ₑ'); t1:= replace(t1, '2091', 'ₑ');
        t1:= replace(t1, '0259', 'ₔ'); // sub schwa
        // note no sub H (upper case) yet
        t1:= replace(t1, 'H', 'ₕ'); t1:= replace(t1, '0048', 'ₕ');
        t1:= replace(t1, 'h', 'ₕ'); t1:= replace(t1, '0068', 'ₕ'); t1:= replace(t1, '2095', 'ₕ');
        // note no sub K (upper case) yet
        t1:= replace(t1, 'K', 'ₖ'); t1:= replace(t1, '004B', 'ₖ');
        t1:= replace(t1, 'k', 'ₖ'); t1:= replace(t1, '006B', 'ₖ'); t1:= replace(t1, '2096', 'ₖ');
        // note no sub L (upper case) yet
        t1:= replace(t1, 'L', 'ₗ'); t1:= replace(t1, '004C', 'ₗ');
        t1:= replace(t1, 'l', 'ₗ'); t1:= replace(t1, '006C', 'ₗ'); t1:= replace(t1, '2097', 'ₗ');
        // note no sub M (upper case) yet
        t1:= replace(t1, 'M', 'ₘ'); t1:= replace(t1, '004D', 'ₘ');
        t1:= replace(t1, 'm', 'ₘ'); t1:= replace(t1, '006D', 'ₘ'); t1:= replace(t1, '2098', 'ₘ');
        // note no sub N (upper case) yet
        t1:= replace(t1, 'N', 'ₙ'); t1:= replace(t1, '004E', 'ₙ');
        t1:= replace(t1, 'n', 'ₙ'); t1:= replace(t1, '006E', 'ₙ'); t1:= replace(t1, '2099', 'ₙ');
        // note no sub O (upper case) yet
        t1:= replace(t1, 'O', 'ₒ'); t1:= replace(t1, '004F', 'ₒ'); 
        t1:= replace(t1, 'o', 'ₒ'); t1:= replace(t1, '006F', 'ₒ'); t1:= replace(t1, '2092', 'ₒ');
        // note no sub P (upper case) yet
        t1:= replace(t1, 'P', 'ₚ'); t1:= replace(t1, '0050', 'ₚ');
        t1:= replace(t1, 'p', 'ₚ'); t1:= replace(t1, '0070', 'ₚ'); t1:= replace(t1, '209A', 'ₚ');
        // note no sub S (upper case) yet
        t1:= replace(t1, 'S', 'ₛ'); t1:= replace(t1, '0053', 'ₛ');
        t1:= replace(t1, 's', 'ₛ'); t1:= replace(t1, '0073', 'ₛ'); t1:= replace(t1, '209B', 'ₛ');
        // note no sub T (upper case) yet
        t1:= replace(t1, 'T', 'ₜ'); t1:= replace(t1, '0054', 'ₜ');
        t1:= replace(t1, 't', 'ₜ'); t1:= replace(t1, '0074', 'ₜ'); t1:= replace(t1, '209C', 'ₜ');
        // note no sub X (upper case) yet
        t1:= replace(t1, 'X', 'ₓ'); t1:= replace(t1, '0058', 'ₓ');
        t1:= replace(t1, 'x', 'ₓ'); t1:= replace(t1, '0078', 'ₓ'); t1:= replace(t1, '2093', 'ₓ');
        t1:= replace(t1, '0', '₀'); t1:= replace(t1, '0030', '₀'); // sub 0
        t1:= replace(t1, '1', '₁'); t1:= replace(t1, '0031', '₁'); // sub 1
        t1:= replace(t1, '2', '₂'); t1:= replace(t1, '0032', '₂'); // sub 2
        t1:= replace(t1, '3', '₃'); t1:= replace(t1, '0033', '₃'); // sub 3
        t1:= replace(t1, '4', '₄'); t1:= replace(t1, '0034', '₄'); // sub 4
        t1:= replace(t1, '5', '₅'); t1:= replace(t1, '0035', '₅'); // sub 5
        t1:= replace(t1, '6', '₆'); t1:= replace(t1, '0036', '₆'); // sub 6
        t1:= replace(t1, '7', '₇'); t1:= replace(t1, '0037', '₇'); // sub 7
        t1:= replace(t1, '8', '₈'); t1:= replace(t1, '0038', '₈'); // sub 8
        t1:= replace(t1, '9', '₉'); t1:= replace(t1, '0039', '₉'); // sub 9
        t1:= replace(t1, '+', '₊'); t1:= replace(t1, '002B', '₊'); // sub +
        t1:= replace(t1, '-', '₋'); t1:= replace(t1, '002D', '₋'); // sub -
        t1:= replace(t1, '=', '₌'); t1:= replace(t1, '003D', '₌'); // sub =
        t1:= replace(t1, '(', '₍'); t1:= replace(t1, '0028', '₍'); // sub left parenthesis
        t1:= replace(t1, ')', '₎'); t1:= replace(t1, '0029', '₎'); // sub right parenthesis
        t:= t1 + split(t,subE)[2];
    );
    stringvar output:= output + t
);

// this part deals with superscript
s:= output; 
i:= 0;
output:= '';
stringvar array sa:= split(s, supS);
while numbervar i < ubound(sa) do (
    i:= i + 1;
    stringvar t:= sa[i];
    if instr(t, supE) > 0 then (
        stringvar t1:= split(t,supE)[1];
        t1:= replace(t1, 'A', 'ᴬ'); t1:= replace(t1, '0041', 'ᴬ'); t1:= replace(t1, '1D2C', 'ᴬ'); 
        t1:= replace(t1, 'a', 'ᵃ'); t1:= replace(t1, '1D43', 'ⁱ'); 
        t1:= replace(t1, 'B', 'ᴮ'); t1:= replace(t1, '0042', 'ᴬ'); t1:= replace(t1, '1D2E', 'ᴮ'); 
        t1:= replace(t1, 'b', 'ᵇ'); t1:= replace(t1, '1D47', 'ᵇ');  
        // note no sup C (upper case) yet 
        t1:= replace(t1, 'C', 'ᶜ'); t1:= replace(t1, '0043', 'ᶜ'); t1:= replace(t1, '1D9C', 'ᶜ'); 
        t1:= replace(t1, 'c', 'ᶜ'); t1:= replace(t1, '0063', 'ᶜ'); 
        t1:= replace(t1, 'D', 'ᴰ'); t1:= replace(t1, '0044', 'ᴰ'); t1:= replace(t1, '1D30', 'ᴰ');
        t1:= replace(t1, 'd', 'ᵈ'); t1:= replace(t1, '1D48', 'ᵈ'); 
        t1:= replace(t1, 'E', 'ᴱ'); t1:= replace(t1, '0045', 'ᴱ'); t1:= replace(t1, '1D31', 'ᴱ');
        t1:= replace(t1, 'e', 'ᵉ'); t1:= replace(t1, '0065', 'ᵉ'); t1:= replace(t1, '1D49', 'ᵉ');
        // note no sup F (upper case) yet
        t1:= replace(t1, 'F', 'ᶠ'); t1:= replace(t1, '0046', 'ᶠ');
        t1:= replace(t1, 'f', 'ᶠ'); t1:= replace(t1, '0066', 'ᶠ'); t1:= replace(t1, '1DA0', 'ᶠ');
        t1:= replace(t1, 'G', 'ᴳ'); t1:= replace(t1, '0047', 'ᴳ'); t1:= replace(t1, '1D33', 'ᴳ');
        t1:= replace(t1, 'g', 'ᵍ'); t1:= replace(t1, '0067', 'ᵍ'); t1:= replace(t1, '1D4S', 'ᵍ');
        t1:= replace(t1, 'H', 'ᴴ'); t1:= replace(t1, '0048', 'ᴴ'); t1:= replace(t1, '1D34', 'ᴴ');
        t1:= replace(t1, 'h', 'ʰ'); t1:= replace(t1, '0068', 'ʰ'); t1:= replace(t1, '02B0', 'ʰ');
        t1:= replace(t1, 'I', 'ᴵ'); t1:= replace(t1, '0049', 'ᴵ'); t1:= replace(t1, '1D35', 'ᴵ');
        t1:= replace(t1, 'i', 'ⁱ'); t1:= replace(t1, '0069', 'ⁱ'); t1:= replace(t1, '2071', 'ⁱ');
        t1:= replace(t1, 'J', 'ᴶ'); t1:= replace(t1, '004A', 'ᴶ'); t1:= replace(t1, '1D36', 'ᴶ');
        t1:= replace(t1, 'j', 'ʲ'); t1:= replace(t1, '006A', 'ʲ'); t1:= replace(t1, '02B2', 'ʲ');
        t1:= replace(t1, 'K', 'ᴷ'); t1:= replace(t1, '004B', 'ᴷ'); t1:= replace(t1, '1D37', 'ᴷ');
        t1:= replace(t1, 'k', 'ᵏ'); t1:= replace(t1, '006B', 'ᵏ'); t1:= replace(t1, '1D4F', 'ᵏ');
        t1:= replace(t1, 'L', 'ᴸ'); t1:= replace(t1, '004C', 'ᴸ'); t1:= replace(t1, '1D38', 'ᴸ');
        t1:= replace(t1, 'l', 'ˡ'); t1:= replace(t1, '006C', 'ˡ'); t1:= replace(t1, '02E1', 'ˡ');
        t1:= replace(t1, 'M', 'ᴹ'); t1:= replace(t1, '004D', 'ᴹ'); t1:= replace(t1, '1D39', 'ᴹ');
        t1:= replace(t1, 'm', 'ᵐ'); t1:= replace(t1, '006D', 'ᵐ'); t1:= replace(t1, '1D50', 'ᵏ');
        t1:= replace(t1, 'N', 'ᴺ'); t1:= replace(t1, '004E', 'ᴺ'); t1:= replace(t1, '1D3A', 'ᴺ');
        t1:= replace(t1, 'n', 'ⁿ'); t1:= replace(t1, '006E', 'ⁿ'); t1:= replace(t1, '207F', 'ᵏ');
        t1:= replace(t1, 'O', 'ᴼ'); t1:= replace(t1, '004F', 'ᴼ'); t1:= replace(t1, '1D3C', 'ᴼ');
        t1:= replace(t1, 'o', 'ᵒ'); t1:= replace(t1, '006F', 'ᵒ'); t1:= replace(t1, '1D52', 'ᵒ');
        t1:= replace(t1, 'P', 'ᴾ'); t1:= replace(t1, '0050', 'ᴾ'); t1:= replace(t1, '1D3E', 'ᴾ');
        t1:= replace(t1, 'p', 'ᵖ'); t1:= replace(t1, '0070', 'ᵖ'); t1:= replace(t1, '1D56', 'ᵖ');
        // note no sup Q (upper case) yet
        t1:= replace(t1, 'Q', 'Q'); t1:= replace(t1, '0051', 'Q');
        // note no sup q (lower case) yet
        t1:= replace(t1, 'q', 'q'); t1:= replace(t1, '0071', 'q');
        t1:= replace(t1, 'R', 'ᴿ'); t1:= replace(t1, '0052', 'ᴿ'); t1:= replace(t1, '1D3F', 'ᴿ');
        t1:= replace(t1, 'r', 'ʳ'); t1:= replace(t1, '0072', 'ʳ'); t1:= replace(t1, '02B3', 'ʳ');
        // note no sup S (upper case)
        t1:= replace(t1, 'S', 'ˢ'); t1:= replace(t1, '0053', 'ˢ');
        t1:= replace(t1, 's', 'ˢ'); t1:= replace(t1, '0073', 'ˢ'); t1:= replace(t1, '02E2', 'ˢ');
        t1:= replace(t1, 'T', 'ᵀ'); t1:= replace(t1, '0054', 'ᵀ'); t1:= replace(t1, '1D40', 'ᵀ');
        t1:= replace(t1, 't', 'ᵗ'); t1:= replace(t1, '0074', 'ᵗ'); t1:= replace(t1, '1D57', 'ʳᵗ');
        t1:= replace(t1, 'U', 'ᵁ'); t1:= replace(t1, '0055', 'ᵁ'); t1:= replace(t1, '1D41', 'ᵁ');
        t1:= replace(t1, 'u', 'ᵘ'); t1:= replace(t1, '0075', 'ᵘ'); t1:= replace(t1, '1D58', 'ᵘ');
        // note no sup V (upper case) yet
        t1:= replace(t1, 'V', 'ᵛ'); t1:= replace(t1, '0056', 'ᵛ');
        t1:= replace(t1, 'v', 'ᵛ'); t1:= replace(t1, '0072', 'ᵛ'); t1:= replace(t1, '02B3', 'ᵛ');
        t1:= replace(t1, 'W', 'ᵂ'); t1:= replace(t1, '0057', 'ᵂ'); t1:= replace(t1, '1D42', 'ᵂ');
        t1:= replace(t1, 'w', 'ʷ'); t1:= replace(t1, '0077', 'ʷ'); t1:= replace(t1, '02B7', 'ʷ');
        // note no sup X (upper case) yet
        t1:= replace(t1, 'X', 'ˣ'); t1:= replace(t1, '0058', 'ˣ');
        t1:= replace(t1, 'x', 'ˣ'); t1:= replace(t1, '0078', 'ˣ'); t1:= replace(t1, '02E3', 'ˣ');
        // note no sup Y (upper case) yet
        t1:= replace(t1, 'Y', 'ʸ'); t1:= replace(t1, '0059', 'ʸ');
        t1:= replace(t1, 'y', 'ʸ'); t1:= replace(t1, '0079', 'ʸ'); t1:= replace(t1, '02B8', 'ʸ');
        // note no sup Z (upper case) yet
        t1:= replace(t1, 'Z', 'ᶻ'); t1:= replace(t1, '005A', 'ᶻ');
        t1:= replace(t1, 'z', 'ᶻ'); t1:= replace(t1, '007A', 'ᶻ'); t1:= replace(t1, '1DBB', 'ʳ');
        t1:= replace(t1, '0', '⁰'); t1:= replace(t1, '0030', '⁰');
        t1:= replace(t1, '1', '¹'); t1:= replace(t1, '0031', '¹');
        t1:= replace(t1, '2', '²'); t1:= replace(t1, '0032', '²');
        t1:= replace(t1, '3', '³'); t1:= replace(t1, '0033', '³');
        t1:= replace(t1, '4', '⁴'); t1:= replace(t1, '0034', '⁴');
        t1:= replace(t1, '5', '⁵'); t1:= replace(t1, '0035', '⁵');
        t1:= replace(t1, '6', '⁶'); t1:= replace(t1, '0036', '⁶');
        t1:= replace(t1, '7', '⁷'); t1:= replace(t1, '0037', '⁷');
        t1:= replace(t1, '8', '⁸'); t1:= replace(t1, '0038', '⁸');
        t1:= replace(t1, '9', '⁹'); t1:= replace(t1, '0039', '⁹');
        t1:= replace(t1, '+', '⁺'); t1:= replace(t1, '002B', '⁺');
        t:= t1 + split(t,supE)[2];
    );
    stringvar output:= output + t
);

output

This is the sample text in the formula above that we would like to transform.

s:= 'The human body is up to 75% H<sub>2</sub>O. O<sub>2</sub> accounts for about 20% of our air. Eight is 2<sup>3</sup>. This is the N<sub>th</sub> time. That was the 3<sup>rd</sup>.';

This will produce the following on a report, using DejaVu Sans font for the formula.

If you do see some characters missing that you would like to add to this formula, please let me know. They would have to be characters that can be converted via a corresponding font character like the above character sets.

11 Comments
You must be Logged on to comment or reply to a post.
  • Very nice! This works for me. I have been asked to provide superscript versions for ordinals. I.e., 1st, 2nd, 3rd, 4th. That’s just the alpha portions.

    Steve

    • thanks Steve. i haven’t found a free font that has letters in the subscript and superscript block. i created the above using Arial Unicode MS but it does not have those characters unfortunately. let me know if you can find a free font that does have those characters.

      -jamie

  • Hi,

    I was wondering, for the superscript, how come you posted numbers 1-3 with an html tag of ‘&sub1;’ but for numbers 4-9 you used what looks like a smaller raised font.  At least, that’s what it looks like in the source code.  The reason I ask is when viewing on the screen in Crystal all the numbers work, but when printing to a PDF or a printer numbers 4-9 don’t render, while 1-3 do.  Thanks.

    • hi Alexander and thanks for your comment. All of the subscript and superscript parts were copied and pasted directly from Windows > Character Map > Arial Unicode MS > Super/Subscript Unicode Range.

      I just double-checked and although I do see the same unusual looking spacing in the numbers greater than 3, the PDF export works fine for me. I use the test string of

      s:= ‘start sub <sub>0123456789+-=()</sub> endsub. start sup <sup>0123456789+</sup> end sup.’;

      I’m using version 14.1.5.1501 of CR.

      If you’re using a copy of CR that hasn’t been updated in a while perhaps try to update to see if that solves the export problem.

      • I see what you mean.  When exporting from Crystal the superscript #4 is showing up, I usually print my reports from Business Objects InfoView, and from there I’m getting the box instead of the superscript 4.  Given that, do you think it’s likely and issue with IE printing a character that is not actually within the font (my font doesn’t have superscript 4, only 1,2,3).  I realize this may be outside youre expertise I’m just looking for any info on this I can get to try to get my head around my options.  Thanks again, for both the code and the replies.

        • That would definitely be an issue regarding the font as this whole technique requires the proper font for the output. I’m not sure what OS you’re running your InfoView on but you may wish to use Arial Unicode MS as a test.

      • Dear Sir,

        Thanks in advance!!

        when am declaring as the below code in my crystal report formula as described above, its gives error to me. how to solve the same.(A number,currency,Boolean or string etc is expected here) is the error message.

        stringvar subS:= ‘<sub>’; // beginning of a subscript

         

        Regards,

        Sajeesh

        • hello Sajeesh,

          Thanks very much for letting me know. In the scn migration the ‘ (chr 39) in the formula became a  ‘ (chr 145?). I’ve changed the formula in the post and set it to use the code formatting instead. It should be working now.

          regards,

          -jamie

          • Dear Sir,

            Thank you very much for your reply.

            As per the new code specified above I corrected the report and got the result what i needed.

            Thanks Once again.

            Regards,

            Sajeesh

             

  • Dear Jamie Sir,

     

    Once again thanks in advance. I have a doubt, its related to crystal report export to pdf.

    I need to print barcode in crystal report and need to export to pdf. i am using ID AutomationHC39M font for printing the barcode.Its printing the barcode in crystal report, but when i am exporting to pdf to thake the print out,it showing blank instead of barcode. Please help me .

    Regards,

    Sajeesh

    • hi Sajeesh, I hope that you are doing well. Sorry to do this but you should post this as a new question to the scn crystal forum as per the SCN rules.

      First though have a look at IDAutomation’s support site to see if there’s an answer related to your setup via this link.

      regards, -jamie