/* AbiWord * Copyright (C) 1998 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include "ap_Features.h" #include "ut_debugmsg.h" #include "ut_assert.h" #include "ut_types.h" #include "ut_string.h" #include "xap_App.h" #include "xap_Preview.h" #include "ap_Preview_Paragraph.h" #include "ap_Strings.h" #include "ap_Dialog_Lists.h" #include "gr_Painter.h" /************************************************************************/ // all of these are measured in pixels #define DEFAULT_TOP_MARGIN m_gc->tlu(5) #define DEFAULT_LEFT_STOP m_gc->tlu(20) #define DEFAULT_RIGHT_STOP m_gc->tlu(20) #define DEFAULT_BEFORE_SPACING 0 #define DEFAULT_AFTER_SPACING 0 #define DEFAULT_LINE_SPACING 0 AP_Preview_Paragraph_Block::AP_Preview_Paragraph_Block(UT_RGBColor & clr, GR_Graphics * gc, AP_Dialog_Paragraph::tAlignState align, UT_uint32 fontHeight) { UT_return_if_fail (gc); m_clr.m_red = clr.m_red; m_clr.m_grn = clr.m_grn; m_clr.m_blu = clr.m_blu; m_gc = gc; m_firstLineLeftStop = DEFAULT_LEFT_STOP; m_leftStop = DEFAULT_LEFT_STOP; m_rightStop = DEFAULT_RIGHT_STOP; m_beforeSpacing = DEFAULT_BEFORE_SPACING; m_afterSpacing = DEFAULT_AFTER_SPACING; m_lineSpacing = DEFAULT_LINE_SPACING; m_align = align; m_indent = AP_Dialog_Paragraph::indent_NONE; m_spacing = AP_Dialog_Paragraph::spacing_SINGLE; m_fontHeight = fontHeight; } AP_Preview_Paragraph_Block::~AP_Preview_Paragraph_Block() { // clear out our vector by freeing ONLY the first // word. All the pointers in the vector point // to different offsets of one piece of memory allocated // all at once. if (m_words.getItemCount() > 0) { UT_UCSChar * word = m_words.getNthItem(0); FREEP(word); } } void AP_Preview_Paragraph_Block::setText(const UT_UCSChar * text) { UT_return_if_fail (text); // clear the words vector by freeing ONLY the first // word (see the destructor for notes) if (m_words.getItemCount() > 0) { UT_UCSChar * word = m_words.getNthItem(0); FREEP(word); m_words.clear(); } // clear the widths vector (has no memory at each item) m_widths.clear(); // dup the string for harmful chunkification UT_UCSChar * clone = NULL; UT_UCS4_cloneString(&clone, text); UT_UCSChar * i = clone; while (*i != 0) { // for each space if (*i == ' ') { // terminate this word *i = 0; // add clone item m_words.addItem(clone); // measure clone item m_widths.addItem(m_gc->measureString(clone, 0, UT_UCS4_strlen(clone), NULL)); // advance clone pointer for new word clone = i + 1; } i++; } // add last word m_words.addItem(clone); // measure last word m_widths.addItem(m_gc->measureString(clone, 0, UT_UCS4_strlen(clone), NULL)); } // ignores NULL parameters, otherwise scales dimensioned strings into // pixel constants for drawing this block. It's 72/2, since we'll // be converting all units to inches, then to points (72 dpi), // and cutting in half (/2) for mini-view #define DIMENSION_INCH_SCALE_FACTOR 36 #define STORE_CONVERTED(m, v) \ if (v) m = (UT_uint32) (UT_convertToInches(v) * (double) DIMENSION_INCH_SCALE_FACTOR); #define SCALE_TO_PIXELS(s) ((UT_uint32) (UT_convertToInches(s) * (double) DIMENSION_INCH_SCALE_FACTOR)) void AP_Preview_Paragraph_Block::setFormat(const gchar * pageLeftMargin, const gchar * pageRightMargin, AP_Dialog_Paragraph::tAlignState align, const gchar * firstLineIndent, AP_Dialog_Paragraph::tIndentState indent, const gchar * leftIndent, const gchar * rightIndent, const gchar * beforeSpacing, const gchar * afterSpacing, const gchar * lineSpacing, AP_Dialog_Paragraph::tSpacingState spacing) { // align is always set m_align = align; if(pageLeftMargin) { m_leftStop = m_gc->tlu(SCALE_TO_PIXELS(pageLeftMargin)); } else { m_leftStop = DEFAULT_LEFT_STOP; } // left margins are in or out from the default stop if (leftIndent) { m_leftStop += m_gc->tlu(SCALE_TO_PIXELS(leftIndent)); // NOTE : if we recomputed the leftIndent, we have to recompute // NOTE : the firstLineLeftStop below } if(pageRightMargin) { m_rightStop = m_gc->tlu(SCALE_TO_PIXELS(pageRightMargin)); } else { m_rightStop = DEFAULT_RIGHT_STOP; } // right margins are in or out from the default stop if (rightIndent) m_rightStop += m_gc->tlu(SCALE_TO_PIXELS(rightIndent)); STORE_CONVERTED(m_beforeSpacing, beforeSpacing); STORE_CONVERTED(m_afterSpacing, afterSpacing); m_beforeSpacing = m_gc->tlu(m_beforeSpacing); m_afterSpacing = m_gc->tlu(m_afterSpacing); m_indent = indent; switch (m_indent) { // the signage for these two is handled in the conversion through // UT_convertToInches() case AP_Dialog_Paragraph::indent_FIRSTLINE: m_firstLineLeftStop = m_leftStop + m_gc->tlu(SCALE_TO_PIXELS(firstLineIndent)); break; case AP_Dialog_Paragraph::indent_HANGING: m_firstLineLeftStop = m_leftStop - m_gc->tlu(SCALE_TO_PIXELS(firstLineIndent)); break; case AP_Dialog_Paragraph::indent_NONE: m_firstLineLeftStop = m_leftStop; break; default: UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } if (lineSpacing) { m_spacing = spacing; switch (m_spacing) { case AP_Dialog_Paragraph::spacing_UNDEF: case AP_Dialog_Paragraph::spacing_SINGLE: m_lineSpacing = 0; break; case AP_Dialog_Paragraph::spacing_ONEANDHALF: m_lineSpacing = (UT_uint32) ((double) m_fontHeight * (double) 0.5); break; case AP_Dialog_Paragraph::spacing_DOUBLE: m_lineSpacing = m_fontHeight; break; case AP_Dialog_Paragraph::spacing_ATLEAST: // TODO : THIS IS BROKEN SOMEHOW. m_lineSpacing should be the number // TODO : of pixels needed to place before the line (of height // TODO : m_fontHeight pixels). // we measure from top to top here, and use a minimum of the current // line height if (m_gc->tlu(SCALE_TO_PIXELS(lineSpacing)) > static_cast(m_fontHeight)) m_lineSpacing = m_gc->tlu(SCALE_TO_PIXELS(lineSpacing)) - m_fontHeight; else m_lineSpacing = 0; break; case AP_Dialog_Paragraph::spacing_EXACTLY: // for exact, we always give them exactly what they asked for, // overlapping and all m_lineSpacing = m_gc->tlu(SCALE_TO_PIXELS(lineSpacing)); break; case AP_Dialog_Paragraph::spacing_MULTIPLE: m_lineSpacing = (UT_uint32) ((double) m_fontHeight * (UT_convertDimensionless(lineSpacing) - (double) 1)); break; } } } /************************************************************************/ AP_Preview_Paragraph::AP_Preview_Paragraph(GR_Graphics * gc, const UT_UCSChar * text, AP_Dialog_Lists * dlg) : XAP_Preview(gc), m_dir(UT_BIDI_LTR) { UT_ASSERT_HARMLESS(text && dlg); m_font = NULL; m_fontHeight = 0; m_y = DEFAULT_TOP_MARGIN; m_clrWhite = new UT_RGBColor(255,255,255); m_clrBlack = new UT_RGBColor(0,0,0); m_clrGray = new UT_RGBColor(192,192,192); // initialize font to start measuring with for following setText calls _loadDrawFont(); { // this block is a dummy block m_previousBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); m_previousBlock->setFormat(NULL, NULL, AP_Dialog_Paragraph::align_LEFT, NULL, AP_Dialog_Paragraph::indent_NONE, NULL,NULL,NULL,NULL,NULL, AP_Dialog_Paragraph::spacing_SINGLE); } { // this block is our ACTIVE block m_activeBlock = new AP_Preview_Paragraph_Block(*m_clrBlack, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); // read these from the dialog's members #if 0 m_activeBlock->setFormat(NULL, NULL, (AP_Dialog_Paragraph::tAlignState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_ALIGNMENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_SPECIAL_INDENT), (AP_Dialog_Paragraph::tIndentState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_SPECIAL_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_LEFT_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_RIGHT_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_BEFORE_SPACING), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_AFTER_SPACING), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_SPECIAL_SPACING), (AP_Dialog_Paragraph::tSpacingState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_SPECIAL_SPACING)); #endif } { // another dummy block m_followingBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); m_followingBlock->setFormat(NULL, NULL, AP_Dialog_Paragraph::align_LEFT, NULL, AP_Dialog_Paragraph::indent_NONE, NULL,NULL,NULL,NULL,NULL, AP_Dialog_Paragraph::spacing_SINGLE); } const XAP_StringSet * pSS = dlg->getApp()->getStringSet(); UT_UTF8String s; pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewPrevParagraph,s); m_previousBlock->setText(s.ucs4_str().ucs4_str()); // this text came from the current document, passed in as arg m_activeBlock->setText(text); pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewFollowParagraph,s); m_followingBlock->setText(s.ucs4_str().ucs4_str()); } AP_Preview_Paragraph::AP_Preview_Paragraph(GR_Graphics * gc, const UT_UCSChar * text, XAP_Dialog * dlg) : XAP_Preview(gc), m_dir(UT_BIDI_LTR) { // this method heavily relies upon the parent dlg to call setFormat() // rather than auto-generating defaults UT_ASSERT_HARMLESS(text && dlg); m_font = NULL; m_fontHeight = 0; m_y = DEFAULT_TOP_MARGIN; m_clrWhite = new UT_RGBColor(255,255,255); m_clrBlack = new UT_RGBColor(0,0,0); m_clrGray = new UT_RGBColor(192,192,192); // initialize font to start measuring with for following setText calls _loadDrawFont(); { // this block is a dummy block m_previousBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); } { // this block is our ACTIVE block m_activeBlock = new AP_Preview_Paragraph_Block(*m_clrBlack, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); } { // another dummy block m_followingBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); } const XAP_StringSet * pSS = dlg->getApp()->getStringSet(); UT_UTF8String s; pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewPrevParagraph,s); m_previousBlock->setText(s.ucs4_str().ucs4_str()); // this text came from the current document, passed in as arg m_activeBlock->setText(text); pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewFollowParagraph,s); m_followingBlock->setText(s.ucs4_str().ucs4_str()); } AP_Preview_Paragraph::AP_Preview_Paragraph(GR_Graphics * gc, const UT_UCSChar * text, AP_Dialog_Paragraph * dlg) : XAP_Preview(gc),m_dir(UT_BIDI_LTR) { UT_ASSERT_HARMLESS(text && dlg); m_font = NULL; m_fontHeight = 0; m_y = DEFAULT_TOP_MARGIN; m_clrWhite = new UT_RGBColor(255,255,255); m_clrBlack = new UT_RGBColor(0,0,0); m_clrGray = new UT_RGBColor(192,192,192); // initialize font to start measuring with for following setText calls _loadDrawFont(); { // this block is a dummy block m_previousBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); m_previousBlock->setFormat(dlg->m_pageLeftMargin, dlg->m_pageRightMargin, (AP_Dialog_Paragraph::tAlignState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_ALIGNMENT), NULL, AP_Dialog_Paragraph::indent_NONE, NULL,NULL,NULL,NULL,NULL, AP_Dialog_Paragraph::spacing_SINGLE); } { // this block is our ACTIVE block m_activeBlock = new AP_Preview_Paragraph_Block(*m_clrBlack, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); // read these from the dialog's members m_activeBlock->setFormat(dlg->m_pageLeftMargin, dlg->m_pageRightMargin, (AP_Dialog_Paragraph::tAlignState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_ALIGNMENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_SPECIAL_INDENT), (AP_Dialog_Paragraph::tIndentState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_SPECIAL_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_LEFT_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_RIGHT_INDENT), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_BEFORE_SPACING), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_AFTER_SPACING), dlg->_getSpinItemValue(AP_Dialog_Paragraph::id_SPIN_SPECIAL_SPACING), (AP_Dialog_Paragraph::tSpacingState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_SPECIAL_SPACING)); if(dlg->_getCheckItemValue(AP_Dialog_Paragraph::id_CHECK_DOMDIRECTION) == AP_Dialog_Paragraph::check_TRUE) m_dir = UT_BIDI_RTL; } { // another dummy block m_followingBlock = new AP_Preview_Paragraph_Block(*m_clrGray, m_gc, AP_Dialog_Paragraph::align_LEFT, m_fontHeight); m_followingBlock->setFormat(dlg->m_pageLeftMargin, dlg->m_pageRightMargin, (AP_Dialog_Paragraph::tAlignState) dlg->_getMenuItemValue(AP_Dialog_Paragraph::id_MENU_ALIGNMENT), NULL, AP_Dialog_Paragraph::indent_NONE, NULL,NULL,NULL,NULL,NULL, AP_Dialog_Paragraph::spacing_SINGLE); } const XAP_StringSet * pSS = dlg->m_pApp->getStringSet(); UT_UTF8String s; pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewPrevParagraph,s); m_previousBlock->setText(s.ucs4_str().ucs4_str()); // this text came from the current document, passed in as arg m_activeBlock->setText(text); pSS->getValueUTF8(AP_STRING_ID_DLG_Para_PreviewFollowParagraph,s); m_followingBlock->setText(s.ucs4_str().ucs4_str()); } AP_Preview_Paragraph::~AP_Preview_Paragraph() { DELETEP(m_clrWhite); DELETEP(m_clrBlack); DELETEP(m_clrGray); DELETEP(m_previousBlock); DELETEP(m_activeBlock); DELETEP(m_followingBlock); } void AP_Preview_Paragraph::setFormat(const gchar * pageLeftMargin, const gchar * pageRightMargin, AP_Dialog_Paragraph::tAlignState align, const gchar * firstLineIndent, AP_Dialog_Paragraph::tIndentState indent, const gchar * leftIndent, const gchar * rightIndent, const gchar * beforeSpacing, const gchar * afterSpacing, const gchar * lineSpacing, AP_Dialog_Paragraph::tSpacingState spacing, UT_BidiCharType dir) { UT_return_if_fail(m_activeBlock); m_dir = dir; m_activeBlock->setFormat(pageLeftMargin, pageRightMargin, align, firstLineIndent, indent, leftIndent, rightIndent, beforeSpacing, afterSpacing, lineSpacing, spacing); } void AP_Preview_Paragraph::draw(void) { UT_return_if_fail (m_gc); // paint white background (Word 97's background is always white, the // text is always black, and the font is always the same size, regardless // of the current document's real block formatting). _drawPageBackground(); // draw the three paragraphs _appendBlock(m_previousBlock); _appendBlock(m_activeBlock); _appendBlock(m_followingBlock); _drawPageBorder(); m_y = DEFAULT_TOP_MARGIN; } bool AP_Preview_Paragraph::_loadDrawFont(void) { // we draw at 7 points in this preview GR_Font * font = m_gc->findFont("Times New Roman", "normal", "", "normal", "", "7pt", NULL); // might need to get the real lang // from somewhere if (font) { m_font = font; m_gc->setFont(m_font); m_fontHeight = m_gc->getFontHeight(); return true; } else return false; } void AP_Preview_Paragraph::_drawPageBackground(void) { // clear area GR_Painter painter(m_gc); painter.fillRect(*m_clrWhite, 0, 0, m_gc->tlu(getWindowWidth()), m_gc->tlu(getWindowHeight())); } void AP_Preview_Paragraph::_drawPageBorder(void) { GR_Painter painter(m_gc); // draw a black one pixel border m_gc->setColor(*m_clrBlack); painter.drawLine(0, 0, m_gc->tlu(getWindowWidth()), 0); painter.drawLine(m_gc->tlu(getWindowWidth()) - m_gc->tlu(1), 0, m_gc->tlu(getWindowWidth()) - m_gc->tlu(1), m_gc->tlu(getWindowHeight())); painter.drawLine(m_gc->tlu(getWindowWidth()) - m_gc->tlu(1), m_gc->tlu(getWindowHeight()) - m_gc->tlu(1), 0, m_gc->tlu(getWindowHeight()) - m_gc->tlu(1)); painter.drawLine(0, m_gc->tlu(getWindowHeight()) - m_gc->tlu(1), 0, 0); } void AP_Preview_Paragraph::_appendBlock(AP_Preview_Paragraph_Block * block) { UT_return_if_fail (block); UT_uint32 ypre = 0; UT_uint32 ypost = 0; UT_sint32 wordCounter = 0; UT_sint32 wordCount = block->m_words.getItemCount(); m_gc->setColor(block->m_clr); { // is there any "at least" or "exactly" spacing for this block? // we have to land the baseline in the right spot then switch (block->m_spacing) { case AP_Dialog_Paragraph::spacing_UNDEF: case AP_Dialog_Paragraph::spacing_SINGLE: case AP_Dialog_Paragraph::spacing_ONEANDHALF: case AP_Dialog_Paragraph::spacing_DOUBLE: case AP_Dialog_Paragraph::spacing_MULTIPLE: ypost = block->m_lineSpacing; break; case AP_Dialog_Paragraph::spacing_ATLEAST: case AP_Dialog_Paragraph::spacing_EXACTLY: ypre = block->m_lineSpacing; break; default: UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } } // start y at m_y; UT_uint32 y = m_y; // do before block spacing y += block->m_beforeSpacing; // handle any spacing before first line y += ypre; // draw first line wordCounter += _appendLine(&block->m_words, &block->m_widths, 0, block->m_firstLineLeftStop, block->m_rightStop, block->m_align, y); y += block->m_fontHeight; // handle any spacing after first line y += ypost; // handle all other lines until out of words UT_uint32 newWords = 1; while (wordCounter < wordCount && newWords > 0) { // handle any spacing before this line y += ypre; newWords = _appendLine(&block->m_words, &block->m_widths, wordCounter, block->m_leftStop, block->m_rightStop, block->m_align, y); wordCounter += newWords; y += block->m_fontHeight; // handle any spacing after this line y += ypost; } // do after block spacing y += block->m_afterSpacing; // record the changes m_y = y; } // returns number of words it plotted UT_uint32 AP_Preview_Paragraph::_appendLine(UT_GenericVector * words, UT_NumberVector * widths, UT_uint32 startWithWord, UT_uint32 left, UT_uint32 right, AP_Dialog_Paragraph::tAlignState align, UT_uint32 y) { UT_return_val_if_fail (words && widths, 0); // width of space character in pixels UT_sint32 spaceCharWidth = m_gc->tlu(3); UT_uint32 i = 0; UT_uint32 totalWords = words->getItemCount(); UT_uint32 pixelsForThisLine = 0; // max length of first line is the diff between the (special) // left stop and the (normal) right stop UT_sint32 maxPixelsForThisLine = m_gc->tlu(getWindowWidth()) - left - right; // negative or zero makes no sense; bail in that case (callers can deal) if (maxPixelsForThisLine <= 0) return 0; i = startWithWord; // while we're not out of words AND have space, try to pack more onto this line // NOTE : we don't evaluate space widths in the while() condition so we don't // NOTE : wrap on one (which would be silly) while ((i < totalWords) && (pixelsForThisLine + widths->getNthItem(i) <= (UT_uint32)maxPixelsForThisLine)) { pixelsForThisLine += widths->getNthItem(i) + spaceCharWidth; i++; } if(i == startWithWord) { // HACK: Make sure we have at least one word. (no longer true, because of above) pixelsForThisLine += widths->getNthItem(i) + spaceCharWidth; i++; } // we have "i" words to plot on this line, and they will take pixelsForThisLine space // TODO : maybe rework following code to remove this variable for more speed UT_uint32 willDrawAt = left; if(m_dir == UT_BIDI_RTL) willDrawAt += maxPixelsForThisLine; spaceCharWidth <<= 8; // Calculate spacing at 256 times the resolution // obey alignment requests switch(align) { case AP_Dialog_Paragraph::align_RIGHT: if(m_dir == UT_BIDI_LTR) // for right, we just draw at the difference in spaces added onto the first line stop. willDrawAt = left + (maxPixelsForThisLine - pixelsForThisLine); break; case AP_Dialog_Paragraph::align_CENTERED: // for centered, we split the difference willDrawAt = left + (maxPixelsForThisLine - pixelsForThisLine) / 2; break; case AP_Dialog_Paragraph::align_JUSTIFIED: if(i < totalWords) { spaceCharWidth += (UT_sint32)((double)(maxPixelsForThisLine - pixelsForThisLine) / (i - startWithWord) * 256); } break; default: // aligh_LEFT is caught here if(m_dir == UT_BIDI_RTL) willDrawAt = pixelsForThisLine + left; break; } willDrawAt <<= 8; UT_uint32 k; GR_Painter painter(m_gc); UT_UCS4String s; UT_UCS4Char *pBuf = NULL; UT_uint32 size = 0; for (k = startWithWord; k < i; k++) { // this will not produce correct results in true bidi text, since the words that are inconsistend // with the overall pargraph direction will be in wrong order, but that is not a big deal s = words->getNthItem(k); size = s.size() + 1; pBuf = (UT_UCS4Char *)UT_calloc(size, sizeof(UT_UCS4Char)); memset(pBuf, 0, size * sizeof(UT_UCS4Char)); UT_bidiReorderString(s.ucs4_str(), s.size(), m_dir, pBuf); if(m_dir == UT_BIDI_RTL) willDrawAt -= ((widths->getNthItem(k)) << 8) + spaceCharWidth; painter.drawChars(pBuf, 0, s.size(), willDrawAt >> 8, y); if(m_dir == UT_BIDI_LTR) willDrawAt += ((widths->getNthItem(k)) << 8) + spaceCharWidth; FREEP(pBuf); } // return number of words drawn return k - startWithWord; }