/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla MathML Project. * * The Initial Developer of the Original Code is The University Of * Queensland. Portions created by The University Of Queensland are * Copyright (C) 1999 The University Of Queensland. All Rights Reserved. * * Contributor(s): * Roger B. Sidje * Shyjan Mahamud */ #include "nsCOMPtr.h" #include "nsHTMLParts.h" #include "nsIHTMLContent.h" #include "nsFrame.h" #include "nsLineLayout.h" #include "nsHTMLIIDs.h" #include "nsIPresContext.h" #include "nsHTMLAtoms.h" #include "nsUnitConversion.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" #include "nsINameSpaceManager.h" #include "nsString.h" #include "nsIRenderingContext.h" #include "nsIFontMetrics.h" #include "nsStyleUtil.h" #include "nsMathMLOperators.h" #include "nsMathMLChar.h" // // nsMathMLChar implementation // // Table of extensible chars in a suitable format for algorithmic purpose // XXX Error-prone stuff. See how it can be automated. /* Structure of the table gMathMLCharGlyph[]: ------------------------------------------ The table is divided into sections, each corresponding to a stretchy symbol. Each section itself is divided into two blocks that are separated with the special null code 0x0000. The first block consists of *any number* of different glyphs of *increasing* size. The second block consists of exactly *4* glyphs that can be used to build a larger size. Another separate table, gMathMLCharIndex[], is used to store pointers to the beginning of each section. Three steps are needed to extend the table: ---------------------------------------=--- 1. Append your new enum in nsMathMLCharList.h 2. Append your new section in gMathMLCharGlyph[]. 3. Append a new pointer in gMathMLCharIndex[], so that gMathMLCharIndex[your_enum] points to the begining of your new section in gMathMLCharGlyph[]. That is, your_section = gMathMLCharGlyph[gMathMLCharIndex[your_enum]] */ // XXX The current values in the table correspond to the Symbol font. // Other fonts will need their own customized tables. // XXX The Stretching doesn't align the glyphs in italic style. Need to // force the style to normal before starting to stretch. static PRUnichar gMathMLCharGlyph[] = { //------------ //index = 0 //Section for: left parenthesis //Block of different variants of *increasing* size... 0x0028, // LEFT PARENTHESIS # parenleft // more here ... 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8EB, // LEFT PAREN TOP # parenlefttp (CUS) 0xF8EC, // LEFT PAREN EXTENDER # parenleftex (CUS) 0xF8ED, // LEFT PAREN BOTTOM # parenleftbt (CUS) 0xF8EC, // glue = LEFT PAREN EXTENDER # parenleftex (CUS) //------------ //index = 6 //Section for: right parenthesis //Block of different variants of *increasing* size... 0x0029, // RIGHT PARENTHESIS # parenright // more here ... 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8F6, // RIGHT PAREN TOP # parenrighttp (CUS) 0xF8F7, // MIDDLE = RIGHT PAREN EXTENDER # parenrightex (CUS) 0xF8F8, // RIGHT PAREN BOTTOM # parenrightbt (CUS) 0xF8F7, // glue = RIGHT PAREN EXTENDER # parenrightex (CUS) //------------ //index = 12 //Section for: integral //Block of different variants of *increasing* size... 0x222B, // INTEGRAL # integral // more here ... 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0x2320, // TOP HALF INTEGRAL # integraltp 0xF8F5, // INTEGRAL EXTENDER # integralex (CUS) 0x2321, // BOTTOM HALF INTEGRAL # integralbt 0xF8F5, // glue = INTEGRAL EXTENDER # integralex (CUS) //------------ //index = 18 //Section for: left square bracket //Block of different variants of *increasing* size... 0x005B, // LEFT SQUARE BRACKET # bracketleft 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8EE, // LEFT SQUARE BRACKET TOP # bracketlefttp (CUS) 0xF8EF, // LEFT SQUARE BRACKET EXTENDER # bracketleftex (CUS) 0xF8F0, // LEFT SQUARE BRACKET BOTTOM # bracketleftbt (CUS) 0xF8EF, // glue = LEFT SQUARE BRACKET EXTENDER # bracketleftex (CUS) //------------ //index = 24 //Section for: right square bracket //Block of different variants of *increasing* size... 0x005D, // RIGHT SQUARE BRACKET # bracketright 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8F9, // RIGHT SQUARE BRACKET TOP # bracketrighttp (CUS) 0xF8FA, // RIGHT SQUARE BRACKET EXTENDER # bracketrightex (CUS) 0xF8FB, // RIGHT SQUARE BRACKET BOTTOM # bracketrightbt (CUS)0 0xF8FA, // glue = RIGHT SQUARE BRACKET EXTENDER # bracketrightex (CUS) //------------ //index = 30 //Section for: left curly bracket //Block of different variants of *increasing* size... 0x007B, // LEFT CURLY BRACKET # braceleft 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8F1, // LEFT CURLY BRACKET TOP # bracelefttp (CUS) 0xF8F2, // LEFT CURLY BRACKET MID # braceleftmid (CUS) 0xF8F3, // LEFT CURLY BRACKET BOTTOM # braceleftbt (CUS) 0xF8F4, // glue = CURLY BRACKET EXTENDER # braceex (CUS) //------------ //index = 36 //Section for: right square bracket //Block of different variants of *increasing* size... 0x007D, // RIGHT CURLY BRACKET # braceright 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8FC, // RIGHT CURLY BRACKET TOP # bracerighttp (CUS) 0xF8FD, // RIGHT CURLY BRACKET MID # bracerightmid (CUS) 0xF8FE, // RIGHT CURLY BRACKET BOTTOM # bracerightbt (CUS) 0xF8F4, // glue = CURLY BRACKET EXTENDER # braceex (CUS) //------------ //index = 42 //Section for: down arrow //Block of different variants of *increasing* size... 0x2193, // DOWNWARDS ARROW # arrowdown 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8E6, // VERTICAL ARROW EXTENDER # arrowvertex (CUS) 0xF8E6, // VERTICAL ARROW EXTENDER # arrowvertex (CUS) 0x2193, // DOWNWARDS ARROW # arrowdown 0xF8E6, // glue = VERTICAL ARROW EXTENDER # arrowvertex (CUS) //------------ //index = 48 //Section for: up arrow //Block of different variants of *increasing* size... 0x2191, // UPWARDS ARROW # arrowup 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0x2191, // UPWARDS ARROW # arrowup 0xF8E6, // VERTICAL ARROW EXTENDER # arrowvertex (CUS) 0xF8E6, // VERTICAL ARROW EXTENDER # arrowvertex (CUS) 0xF8E6, // glue = VERTICAL ARROW EXTENDER # arrowvertex (CUS) //------------ //index = 54 //Section for: left arrow //Block of different variants of *increasing* size... 0x2190, // LEFTWARDS ARROW # arrowleft 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0x2190, // LEFTWARDS ARROW # arrowleft 0xF8E7, // HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) 0xF8E7, // HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) 0xF8E7, // glue = HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) //------------ //index = 60 //Section for: right arrow //Block of different variants of *increasing* size... 0x2192, // RIGHTWARDS ARROW # arrowright 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0xF8E7, // HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) 0xF8E7, // HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) 0x2192, // RIGHTWARDS ARROW # arrowright 0xF8E7, // glue = HORIZONTAL ARROW EXTENDER # arrowhorizex (CUS) //------------ //index = 66 //Section for: square-root bar 0x00AF, // RADICAL EXTENDER # radicalex (CUS) -- formerly 0xF8E5 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue 0x00AF, // RADICAL EXTENDER # radicalex (CUS) 0x00AF, // RADICAL EXTENDER # radicalex (CUS) 0x00AF, // RADICAL EXTENDER # radicalex (CUS) 0x00AF, // RADICAL EXTENDER # radicalex (CUS) //------------ //index = 72 //Section for: vertical bar (0x2223) '|', 0x0000, // That's all folks ... //Block of partial glyphs in the order top/left, middle, bottom/right, glue '|', '|', '|', '|', //------------ //index = 78 //end of table ... 0x0000, }; // Pointers to the beginning of each section static PRInt32 gMathMLCharIndex[] = { 0, // eMathMLChar_LeftParenthesis 6, // eMathMLChar_RightParenthesis 12, // eMathMLChar_Integral 18, // eMathMLChar_LeftSquareBracket 24, // eMathMLChar_RightSquareBracket 30, // eMathMLChar_LeftCurlyBracket 36, // eMathMLChar_RightCurlyBracket 42, // eMathMLChar_DownArrow, 48, // eMathMLChar_UpArrow, 54, // eMathMLChar_LeftArrow, 60, // eMathMLChar_RightArrow, 66, // eMathMLChar_OverBar, 72, // eMathMLChar_VerticalBar, 78, // eMathMLChar_Sqrt, -- placeholder for now 78, // safeguard, *must* always point at the *end* of gMathMLCharGlyph[] }; static const PRInt32 kMathMLChar[] = { #define MATHML_CHAR(_name, _value, _direction) eMathMLChar_##_name, _value, _direction, #include "nsMathMLCharList.h" #undef MATHML_CHAR }; //--------------------------------------------- // If you call SetData(), it will lookup the enum // of the char and set it for you. void nsMathMLChar::SetData(nsString& aData) { mData = aData; // some assumptions until proven otherwise! mEnum = eMathMLChar_DONT_STRETCH; mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; mBoundingMetrics.Clear(); // lookup the enum ... if (1 == mData.Length()) { PRUnichar ch = mData[0]; PRInt32 count = sizeof(kMathMLChar) / sizeof(kMathMLChar[0]); for (PRInt32 i = 0; i < count; i += 3) { if (ch == kMathMLChar[i+1]) { mEnum = nsMathMLCharEnum(kMathMLChar[i]); mDirection = kMathMLChar[i+2]; break; } } } } // If you call SetEnum(), it will lookup the actual value // of the char and set it for you. void nsMathMLChar::SetEnum(nsMathMLCharEnum aEnum) { mEnum = aEnum; // some assumptions until proven otherwise! mData = ""; mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED; mBoundingMetrics.Clear(); // lookup the data ... if (mEnum != eMathMLChar_DONT_STRETCH) { PRInt32 count = sizeof(kMathMLChar) / sizeof(kMathMLChar[0]); for (PRInt32 i = 0; i < count; i += 3) { if (mEnum == kMathMLChar[i]) { mData = PRUnichar(kMathMLChar[i+1]); mDirection = kMathMLChar[i+2]; break; } } } NS_ASSERTION(mData.Length(), "Something is wrong somewhere"); } /* The Stretch: @param aContainerSize - suggested size for the stretched char @param aDesiredStretchSize - IN/OUT parameter. On input our current size, on output the size after stretching. How it works? The Stretch() method first looks for a glyph of appropriate size; If a glyph is found, it is cached by this object and its size is returned in aDesiredStretchSize. The cached glyph will then be used at the painting stage. If no glyph of appropriate size is found, aDesiredStretchSize is set to aContainerSize and the char is later built by parts, at the painting stage, to fit the size. * When a glyph is found, its size can actually be slighthly larger or smaller than aContainerSize. * When built by parts, the char is stretched to *exactly* fit aContainerSize. [Is this what we want? Can we leave the leading?] In either case, it is the responsibility of the caller to account for the spacing when setting aContainerSize, and to leave the extra spacing when placing the stretched char. */ //#define SHOW_BORDERS 1 // NS_IMETHODIMP nsMathMLChar::Stretch(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext, nsStretchDirection aStretchDirection, nsStretchMetrics& aContainerSize, nsStretchMetrics& aDesiredStretchSize) { nsresult rv = NS_OK; nsStretchDirection aDirection = aStretchDirection; if (aDirection == NS_STRETCH_DIRECTION_DEFAULT) { aDirection = mDirection; } // Set font nsStyleFont font; aStyleContext->GetStyle(eStyleStruct_Font, font); aRenderingContext.SetFont(font.mFont); // cache our bounding metrics and quickly return if there is nothing // special about this char if (eMathMLChar_DONT_STRETCH == mEnum || aDirection != mDirection) { rv = aRenderingContext.GetBoundingMetrics(mData.GetUnicode(), PRUint32(mData.Length()), mBoundingMetrics); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", mData[0], mData[0]&0x00FF); /*getchar();*/ return rv; } return NS_OK; } // check the common situations where stretching is not actually needed // XXX need better criteria if (aDirection == NS_STRETCH_DIRECTION_VERTICAL) { if (aContainerSize.height <= aDesiredStretchSize.height) { mEnum = eMathMLChar_DONT_STRETCH; // ensure that the char later behaves like a normal char rv = aRenderingContext.GetBoundingMetrics(mData.GetUnicode(), PRUint32(mData.Length()), mBoundingMetrics); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", mData[0], mData[0]&0x00FF); /*getchar();*/ return rv; } return NS_OK; } } else if (aDirection == NS_STRETCH_DIRECTION_HORIZONTAL) { if (aContainerSize.width <= aDesiredStretchSize.width) { mEnum = eMathMLChar_DONT_STRETCH; // ensure that the char later behaves like a normal char rv = aRenderingContext.GetBoundingMetrics(mData.GetUnicode(), PRUint32(mData.Length()), mBoundingMetrics); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", mData[0], mData[0]&0x00FF); /*getchar();*/ return rv; } return NS_OK; } } // printf("Container height:%d width:%d ascent:%d descent:%d\n contains height:%d width:%d ascent:%d descent:%d\n", // aContainerSize.height, aContainerSize.width, aContainerSize.ascent, aContainerSize.descent, // aDesiredStretchSize.height, aDesiredStretchSize.width, aDesiredStretchSize.ascent, // aDesiredStretchSize.descent); // XXX Note: there are other symbols that just need to slightly // increase their size, like \Sum nscoord height = aDesiredStretchSize.height; nscoord width = aDesiredStretchSize.width; nscoord ascent = aDesiredStretchSize.ascent; nscoord descent = aDesiredStretchSize.descent; PRInt32 index = gMathMLCharIndex[mEnum]; PRInt32 limit = gMathMLCharIndex[mEnum+1]; // next char starts here... nscoord w, h; // try first to see if there is a glyph of appropriate size ... PRBool sizeOK = PR_FALSE; PRUnichar ch = gMathMLCharGlyph[index++]; nsBoundingMetrics bm, bm0; PRInt32 index0 = index; while (ch && index <= limit) { // printf("Checking size of:%c index:%d\n", ch & 0x00FF, index); rv = aRenderingContext.GetBoundingMetrics(&ch, PRUint32(1), bm); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF); /*getchar();*/ return rv; } // keep track of the default bounding metrics if (index0 == index) bm0 = bm; h = bm.ascent + bm.descent; w = bm.rightBearing - bm.leftBearing; // printf("height:%d width:%d ascent:%d descent:%d\n", // height, bm.width, bm.ascent, bm.descent); // XXX temp hack -- need better criteria if ((aDirection == NS_STRETCH_DIRECTION_VERTICAL && h > height) || (aDirection == NS_STRETCH_DIRECTION_HORIZONTAL && w > width)) { sizeOK = PR_TRUE; descent = bm.descent; ascent = bm.ascent; height = bm.ascent + bm.descent; width = bm.rightBearing - bm.leftBearing; // cache all this information mGlyph = ch; mBoundingMetrics = bm; break; } ch = gMathMLCharGlyph[index++]; } if (!sizeOK) { // see if we can build by parts... mGlyph = 0; PRInt32 i; nscoord a, d; h = w = a = d = 0; float flex[3] = {0.7f, 0.3f, 0.7f}; // XXX hack! // get metrics data to be re-used later nsBoundingMetrics bmdata[4]; for (i = 0; i < 4; i++) { ch = gMathMLCharGlyph[index+i]; rv = aRenderingContext.GetBoundingMetrics(&ch, PRUint32(1), bm); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF); /*getchar();*/ return rv; } bmdata[i] = bm; } if (aDirection == NS_STRETCH_DIRECTION_VERTICAL) { // default is to fill-up the area given to us width = aDesiredStretchSize.width; height = aContainerSize.height; ascent = aContainerSize.ascent; descent = aContainerSize.descent; for (i = 0; i < 4; i++) { bm = bmdata[i]; if (w < bm.width) w = bm.width; if (i < 3) { h += nscoord(flex[i]*(bm.ascent+bm.descent)); // sum heights of the parts... // compute and cache our bounding metrics (vertical stacking!) if (mBoundingMetrics.leftBearing > bm.leftBearing) mBoundingMetrics.leftBearing = bm.leftBearing; if (mBoundingMetrics.rightBearing < bm.rightBearing) mBoundingMetrics.rightBearing = bm.rightBearing; } } if (h <= aContainerSize.height) { // can nicely fit in the available space... width = w; mBoundingMetrics.width = w; mBoundingMetrics.ascent = aContainerSize.ascent; mBoundingMetrics.descent = aContainerSize.descent; } else { // sum of parts doesn't fit in the space... will use a single glyph mEnum = eMathMLChar_DONT_STRETCH; // ensure that the char behaves like a normal char mBoundingMetrics = bm0; return NS_OK; } } else if (aDirection == NS_STRETCH_DIRECTION_HORIZONTAL) { // default is to fill-up the area given to us width = aContainerSize.width; height = aDesiredStretchSize.height; ascent = aDesiredStretchSize.ascent; descent = aDesiredStretchSize.descent; for (i = 0; i < 4; i++) { bm = bmdata[i]; if (0 == i) bm0 = bm; if (a < bm.ascent) a = bm.ascent; if (d < bm.descent) d = bm.descent; if (i < 3) { w += nscoord(flex[i]*(bm.rightBearing-bm.leftBearing)); // sum widths of the parts... // compute and cache our bounding metrics (horizontal stacking) if (0 == i) mBoundingMetrics = bm; else mBoundingMetrics += bm; } } if (w <= aContainerSize.width) { // can nicely fit in the available space... //printf("%04X can nicely fit in the available space...\n", ch); // ascent = a; // height = a + d; mBoundingMetrics.width = aContainerSize.width; mBoundingMetrics.leftBearing = 0; mBoundingMetrics.rightBearing = aContainerSize.width; } else { // sum of parts doesn't fit in the space... will use a single glyph mEnum = eMathMLChar_DONT_STRETCH; // ensure that the char behaves like a normal char mBoundingMetrics = bm0; return NS_OK; } } } aDesiredStretchSize.width = width; aDesiredStretchSize.height = height; aDesiredStretchSize.ascent = ascent; aDesiredStretchSize.descent = descent; return NS_OK; } NS_IMETHODIMP nsMathMLChar::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext) { nsresult rv = NS_OK; nsStyleFont font; nsStyleColor color; aStyleContext->GetStyle(eStyleStruct_Font, font); aStyleContext->GetStyle(eStyleStruct_Color, color); // Set color and font ... aRenderingContext.SetColor(color.mColor); aRenderingContext.SetFont(font.mFont); if (eMathMLChar_DONT_STRETCH == mEnum || NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) { // normal drawing if there is nothing special about this char ... aRenderingContext.DrawString(mData.GetUnicode(), PRUint32(mData.Length()), mRect.x, mRect.y); //printf("Painting %04X like a normal char\n", mData[0]); } else if (0 < mGlyph) { // wow, there is a glyph of appropriate size! //printf("Painting %04X with a glyph of appropriate size\n", mData[0]); aRenderingContext.DrawString(&mGlyph, PRUint32(1), mRect.x, mRect.y); } else { // paint by parts // grab some metrics that will help to adjust the placements ... nscoord fontAscent, fontDescent; nsCOMPtr fm; aRenderingContext.GetFontMetrics(*getter_AddRefs(fm)); fm->GetMaxAscent(fontAscent); fm->GetMaxDescent(fontDescent); // do the painting ... if (NS_STRETCH_DIRECTION_VERTICAL == mDirection) return PaintVertically(aPresContext, aRenderingContext, aStyleContext, fontAscent, fontDescent, mEnum, mRect); else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection) return PaintHorizontally(aPresContext, aRenderingContext, aStyleContext, fontAscent, fontDescent, mEnum, mRect); } return NS_OK; } /* ================================================================================= And now the helper routines that actually do the job of painting the char by parts */ // draw a glyph in a clipped area so that we don't have hairy chars pending outside void nsMathMLChar::DrawGlyph(nsIRenderingContext& aRenderingContext, PRUnichar aGlyph, nscoord aX, nscoord aY, nsRect& aClipRect) { PRBool clipState; aRenderingContext.PushState(); aRenderingContext.SetClipRect(aClipRect, nsClipCombine_kIntersect, clipState); aRenderingContext.DrawString(&aGlyph, PRUint32(1), aX, aY); aRenderingContext.PopState(clipState); } // paint a stretchy char by assembling glyphs vertically nsresult nsMathMLChar::PaintVertically(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext, nscoord fontAscent, nscoord fontDescent, nsMathMLCharEnum aCharEnum, nsRect aRect) { nsresult rv = NS_OK; PRInt32 index = gMathMLCharIndex[aCharEnum]; PRInt32 limit = gMathMLCharIndex[aCharEnum+1]; // next char starts here... // jump past the glyphs of various size... PRUnichar ch = gMathMLCharGlyph[index++]; while (ch && index <= limit) { ch = gMathMLCharGlyph[index++]; } // return if there are no partial glyphs (an erroneous table!) if (!gMathMLCharGlyph[index]) { printf("Erroneous gMathMLCharGlyph table!\n"); return NS_OK; } nscoord dx = aRect.x; nscoord dy = aRect.y; nsRect clipRect; float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); nscoord onePixel = NSIntPixelsToTwips(1, p2t); // get metrics data to be re-used later PRInt32 i; nsBoundingMetrics bm, bmdata[4]; nscoord stride, offset[3], start[3], end[3]; for (i = 0; i < 4; i++) { ch = gMathMLCharGlyph[index+i]; rv = aRenderingContext.GetBoundingMetrics(&ch, PRUint32(1), bm); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF); /*getchar();*/ return rv; } bmdata[i] = bm; // printf("bm for i:%d ch:%04X ascent:%d descent:%d lbearing:%d rbearing:%d width:%d fontAscent:%d fontDescent:%d\n", // i, ch, bm.ascent, bm.descent, bm.leftBearing, bm.rightBearing, bm.width, fontAscent, fontDescent); } for (i = 0; i < 3; i++) { ch = gMathMLCharGlyph[index+i]; bm = bmdata[i]; //printf("Drawing i=%d ch=%04X", i, ch); getchar(); if (0 == i) { // top //coord with line-spacing (i.e., leading) // dy = aRect.y; //coord for exact fit (i.e., no leading) dy = aRect.y + bm.ascent - fontAscent; } else if (1 == i) { // middle // coord with line-spacing (i.e., leading) // dy = aRect.y + (aRect.height - fontAscent - fontDescent // - bmdata[0].ascent - bmdata[2].descent + bm.ascent + bm.descent)/2; // coord for exact fit (i.e., no leading) dy = aRect.y + (aRect.height - (bm.ascent + bm.descent))/2 - (fontAscent - bm.ascent); } else if (2 == i) { // bottom // coord with line-spacing (i.e., leading) // dy = aRect.y + aRect.height - (fontAscent + fontDescent); // coord for exact fit (i.e., no leading) dy = aRect.y + aRect.height - (fontAscent + bm.descent); } clipRect = nsRect(dx, dy, aRect.width, aRect.height); // abcissa that DrawString used offset[i] = dy; // *exact* abcissa where the *top-most* pixel of the glyph is painted start[i] = dy + fontAscent - bm.ascent; // *exact* abcissa where the *bottom-most* pixel of the glyph is painted end[i] = dy + fontAscent + bm.descent; // note: end = start + height } ///////////////////////////////////// // draw top, middle, bottom for (i = 0; i < 3; i++) { ch = gMathMLCharGlyph[index+i]; #ifdef SHOW_BORDERS aRenderingContext.SetColor(NS_RGB(0,0,0)); aRenderingContext.DrawRect(nsRect(dx,start[i],aRect.width+30*(i+1),end[i]-start[i])); #endif dy = offset[i]; if (i==0) clipRect = nsRect(dx, dy, aRect.width, aRect.height); else if (i==1) clipRect = nsRect(dx, end[0], aRect.width, start[2]-end[0]); else if (i==2) clipRect = nsRect(dx, start[2], aRect.width, end[2]-start[2]); // printf("Drawing i:%d ch:%04X ascent:%d descent:%d lbearing:%d rbearing:%d width:%d at position dx:%d dy:%d\n", // i, ch, bm.ascent, bm.descent, bm.leftBearing, bm.rightBearing, bm.width, dx, dy); // getchar(); if (!clipRect.IsEmpty()) { clipRect.Inflate(onePixel, onePixel); DrawGlyph(aRenderingContext, ch, dx, dy, clipRect); } } /////////////// // fill the gap between top and middle, and between middle and bottom. ch = gMathMLCharGlyph[index+3]; // glue for (i = 0; i < 2; i++) { PRInt32 count = 0; dy = offset[i]; clipRect = nsRect(dx, end[i], aRect.width, start[i+1]-end[i]); clipRect.Inflate(onePixel, onePixel); #ifdef SHOW_BORDERS aRenderingContext.SetColor(NS_RGB(255,0,0)); aRenderingContext.DrawRect(clipRect); #endif bm = bmdata[i]; while (dy + fontAscent + bm.descent < start[i+1]) { if (2 > count) { stride = bm.descent; bm = bmdata[3]; // glue stride += bm.ascent; } count++; dy += stride; DrawGlyph(aRenderingContext, ch, dx, dy, clipRect); // NS_ASSERTION(5000 == count, "Error - gMathMLCharGlyph is incorrectly set"); if (1000 == count) return NS_ERROR_UNEXPECTED; } #ifdef SHOW_BORDERS // last glyph that may cross past its boundary and collide with the next nscoord height = bm.ascent - bm.descent; aRenderingContext.SetColor(NS_RGB(0,255,0)); aRenderingContext.DrawRect(nsRect(dx, dy+fontAscent-bm.ascent, aRect.width, height)); #endif } return NS_OK; } // paint a stretchy char by assembling glyphs horizontally nsresult nsMathMLChar::PaintHorizontally(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, nsIStyleContext* aStyleContext, nscoord fontAscent, nscoord fontDescent, nsMathMLCharEnum aCharEnum, nsRect aRect) { nsresult rv = NS_OK; PRInt32 index = gMathMLCharIndex[aCharEnum]; PRInt32 limit = gMathMLCharIndex[aCharEnum+1]; // next char starts here... // jump past the glyphs of various size... PRUnichar ch = gMathMLCharGlyph[index++]; while (ch && index <= limit) { ch = gMathMLCharGlyph[index++]; } // return if there are no partial glyphs (an erroneous table!) if (!gMathMLCharGlyph[index]) { printf("Erroneous gMathMLCharGlyph table!\n"); return NS_OK; } nscoord dx = aRect.x; nscoord dy = aRect.y; nsRect clipRect; float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); nscoord onePixel = NSIntPixelsToTwips(1, p2t); // get metrics data to be re-used later PRInt32 i; nsBoundingMetrics bm, bmdata[4]; nscoord stride, offset[3], start[3], end[3]; for (i = 0; i < 4; i++) { ch = gMathMLCharGlyph[index+i]; rv = aRenderingContext.GetBoundingMetrics(&ch, PRUint32(1), bm); if (NS_FAILED(rv)) { printf("GetBoundingMetrics failed for %04X:%c\n", ch, ch&0x00FF); /*getchar();*/ return rv; } bmdata[i] = bm; // printf("bm for i:%d ch:%04X ascent:%d descent:%d lbearing:%d rbearing:%d width:%d fontAscent:%d fontDescent:%d\n", // i, ch, bm.ascent, bm.descent, bm.leftBearing, bm.rightBearing, bm.width, fontAscent, fontDescent); } for (i = 0; i < 3; i++) { ch = gMathMLCharGlyph[index+i]; bm = bmdata[i]; //printf("Drawing i=%d ch=%04X", i, ch); getchar(); if (0 == i) { // left // coord with intercharacter-spacing // dx = aRect.x; // coord for exact fit (no intercharacter-spacing) dx = aRect.x - bm.leftBearing; } else if (1 == i) { // middle // coord with intercharacter-spacing dx = aRect.x + (aRect.width - bm.width)/2; } else if (2 == i) { // right // coord with intercharacter-spacing // dx = aRect.x + aRect.width - bm.width; // coord for exact fit (no intercharacter-spacing) dx = aRect.x + aRect.width - bm.rightBearing; } // abcissa that DrawString used offset[i] = dx; // *exact* abcissa where the *left-most* pixel of the glyph is painted start[i] = dx + bm.leftBearing; // *exact* abcissa where the *right-most* pixel of the glyph is painted end[i] = dx + bm.rightBearing; // note: end = start + width } /////////////////////////// // draw left, middle, right for (i = 0; i < 3; i++) { ch = gMathMLCharGlyph[index+i]; #ifdef SHOW_BORDERS aRenderingContext.SetColor(NS_RGB(0,0,0)); aRenderingContext.DrawRect(nsRect(start[i], dy, end[i]-start[i], aRect.height+30*(i+1))); #endif dx = offset[i]; nscoord top = PR_MIN(dy, dy + fontAscent - bm.ascent); if (i==0) clipRect = nsRect(dx, top, aRect.width, aRect.height); else if (i==1) clipRect = nsRect(end[0], top, start[2]-end[0], aRect.height); else if (i==2) clipRect = nsRect(start[2], top, end[2]-start[2], aRect.height); // printf("Drawing i:%d ch:%04X ascent:%d descent:%d lbearing:%d rbearing:%d width:%d at position dx:%d dy:%d\n", // i, ch, bm.ascent, bm.descent, bm.leftBearing, bm.rightBearing, bm.width, dx, dy); // getchar(); if (!clipRect.IsEmpty()) { clipRect.Inflate(onePixel, onePixel); DrawGlyph(aRenderingContext, ch, dx, dy, clipRect); } } //////////////// // fill the gap between left and middle, and between middle and right. ch = gMathMLCharGlyph[index+3]; // glue for (i = 0; i < 2; i++) { PRInt32 count = 0; dx = offset[i]; nscoord top = PR_MIN(dy, dy + fontAscent - bm.ascent); clipRect = nsRect(end[i], top, start[i+1]-end[i], aRect.height); clipRect.Inflate(onePixel, onePixel); #ifdef SHOW_BORDERS // rectangles in-between that are to be filled aRenderingContext.SetColor(NS_RGB(255,0,0)); aRenderingContext.DrawRect(clipRect); #endif bm = bmdata[i]; while (dx + bm.rightBearing < start[i+1]) { if (2 > count) { stride = bm.rightBearing; bm = bmdata[3]; // glue stride -= bm.leftBearing; } count++; dx += stride; DrawGlyph(aRenderingContext, ch, dx, dy, clipRect); // NS_ASSERTION(5000 == count, "Error - gMathMLCharGlyph is incorrectly set"); if (1000 == count) return NS_ERROR_UNEXPECTED; // printf("Drawing %04X ascent:%d descent:%d at position dx:%d dy:%d\n", // ch, bm.ascent, bm.descent, dx, dy); } #ifdef SHOW_BORDERS // last glyph that may cross past its boundary and collide with the next nscoord width = bm.rightBearing - bm.leftBearing; aRenderingContext.SetColor(NS_RGB(0,255,0)); aRenderingContext.DrawRect(nsRect(dx + bm.leftBearing, dy, width, aRect.height)); #endif } return NS_OK; }