?? bidi.cpp
字號:
/**
* This file is part of the html renderer for KDE.
*
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
* Copyright (C) 2004 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "bidi.h"
#include "break_lines.h"
#include "render_block.h"
#include "render_text.h"
#include "render_arena.h"
#include "render_canvas.h"
#include "khtmlview.h"
#include "xml/dom_docimpl.h"
#include "kdebug.h"
#include "qdatetime.h"
#include "qfontmetrics.h"
#define BIDI_DEBUG 0
//#define DEBUG_LINEBREAKS
using DOM::AtomicString;
namespace khtml {
// an iterator which goes through a BidiParagraph
struct BidiIterator
OOM_MODIFIED
{
BidiIterator() : par(0), obj(0), pos(0) {}
BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos) : par(_par), obj(_obj), pos(_pos) {}
void increment( BidiState &bidi );
bool atEnd() const;
const QChar ¤t() const;
QChar::Direction direction() const;
RenderBlock *par;
RenderObject *obj;
unsigned int pos;
};
struct BidiStatus {
BidiStatus() : eor(QChar::DirON), lastStrong(QChar::DirON), last(QChar::DirON) {}
QChar::Direction eor;
QChar::Direction lastStrong;
QChar::Direction last;
};
struct BidiState {
BidiState() : context(0) {}
BidiIterator sor;
BidiIterator eor;
BidiIterator last;
BidiIterator current;
BidiContext *context;
BidiStatus status;
};
// Used to track a list of chained bidi runs.
static BidiRun* sFirstBidiRun;
static BidiRun* sLastBidiRun;
static int sBidiRunCount;
static BidiRun* sCompactFirstBidiRun;
static BidiRun* sCompactLastBidiRun;
static int sCompactBidiRunCount;
static bool sBuildingCompactRuns;
// Midpoint globals. The goal is not to do any allocation when dealing with
// these midpoints, so we just keep an array around and never clear it. We track
// the number of items and position using the two other variables.
static QMemArray<BidiIterator> *smidpoints;
static uint sNumMidpoints;
static uint sCurrMidpoint;
static bool betweenMidpoints;
static bool isLineEmpty = true;
static bool previousLineBrokeCleanly = true;
static QChar::Direction dir;
static bool adjustEmbedding;
static bool emptyRun = true;
static int numSpaces;
static void embed( QChar::Direction d, BidiState &bidi );
static void appendRun( BidiState &bidi );
static int getBPMWidth(int childValue, Length cssUnit)
{
if (cssUnit.type != Variable)
return (cssUnit.type == Fixed ? cssUnit.value : childValue);
return 0;
}
static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
{
RenderStyle* cstyle = child->style();
int result = 0;
bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
(leftSide ? cstyle->marginLeft() :
cstyle->marginRight()));
result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
(leftSide ? cstyle->paddingLeft() :
cstyle->paddingRight()));
result += leftSide ? child->borderLeft() : child->borderRight();
return result;
}
static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
{
int extraWidth = 0;
RenderObject* parent = child->parent();
while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) {
if (start && parent->firstChild() == child)
extraWidth += getBorderPaddingMargin(parent, false);
if (end && parent->lastChild() == child)
extraWidth += getBorderPaddingMargin(parent, true);
child = parent;
parent = child->parent();
}
return extraWidth;
}
#ifndef NDEBUG
static bool inBidiRunDetach;
#endif
void BidiRun::detach(RenderArena* renderArena)
{
#ifndef NDEBUG
inBidiRunDetach = true;
#endif
delete this;
#ifndef NDEBUG
inBidiRunDetach = false;
#endif
// Recover the size left there for us by operator delete and free the memory.
renderArena->free(*(size_t *)this, this);
}
void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
{
return renderArena->allocate(sz);
}
void BidiRun::operator delete(void* ptr, size_t sz)
{
assert(inBidiRunDetach);
// Stash size where detach can find it.
*(size_t*)ptr = sz;
}
static void deleteBidiRuns(RenderArena* arena)
{
if (!sFirstBidiRun)
return;
BidiRun* curr = sFirstBidiRun;
while (curr) {
BidiRun* s = curr->nextRun;
curr->detach(arena);
curr = s;
}
sFirstBidiRun = 0;
sLastBidiRun = 0;
sBidiRunCount = 0;
}
// ---------------------------------------------------------------------
/* a small helper class used internally to resolve Bidi embedding levels.
Each line of text caches the embedding level at the start of the line for faster
relayouting
*/
BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
: level(l) , override(o), dir(e)
{
parent = p;
if(p) {
p->ref();
basicDir = p->basicDir;
} else
basicDir = e;
count = 0;
}
BidiContext::~BidiContext()
{
if(parent) parent->deref();
}
void BidiContext::ref() const
{
count++;
}
void BidiContext::deref() const
{
count--;
if(count <= 0) delete this;
}
// ---------------------------------------------------------------------
inline bool operator==( const BidiIterator &it1, const BidiIterator &it2 )
{
if(it1.pos != it2.pos) return false;
if(it1.obj != it2.obj) return false;
return true;
}
inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 )
{
if(it1.pos != it2.pos) return true;
if(it1.obj != it2.obj) return true;
return false;
}
static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi,
bool skipInlines = true, bool* endOfInline = 0)
{
RenderObject *next = 0;
bool oldEndOfInline = endOfInline ? *endOfInline : false;
if (endOfInline)
*endOfInline = false;
while(current != 0)
{
//kdDebug( 6040 ) << "current = " << current << endl;
if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
next = current->firstChild();
if ( next && adjustEmbedding ) {
EUnicodeBidi ub = next->style()->unicodeBidi();
if ( ub != UBNormal && !emptyRun ) {
EDirection dir = next->style()->direction();
QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
: ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
embed( d, bidi );
}
}
}
if (!next) {
if (!skipInlines && !oldEndOfInline && current->isInlineFlow())
{
next = current;
if (endOfInline)
*endOfInline = true;
break;
}
while (current && current != par) {
next = current->nextSibling();
if (next) break;
if ( adjustEmbedding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
embed( QChar::DirPDF, bidi );
}
current = current->parent();
if (!skipInlines && current && current != par && current->isInlineFlow()) {
next = current;
if (endOfInline)
*endOfInline = true;
break;
}
}
}
if (!next) break;
if (next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned()
|| ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
&& next->isInlineFlow()))
break;
current = next;
}
return next;
}
static RenderObject *first( RenderObject *par, BidiState &bidi, bool skipInlines = true )
{
if(!par->firstChild()) return 0;
RenderObject *o = par->firstChild();
if (o->isInlineFlow()) {
if (skipInlines && o->firstChild())
o = Bidinext( par, o, bidi, skipInlines );
else
return o; // Never skip empty inlines.
}
if (o && !o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
o = Bidinext( par, o, bidi, skipInlines );
return o;
}
inline void BidiIterator::increment (BidiState &bidi)
{
if(!obj) return;
if(obj->isText()) {
pos++;
if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
obj = Bidinext( par, obj, bidi );
pos = 0;
}
} else {
obj = Bidinext( par, obj, bidi );
pos = 0;
}
}
inline bool BidiIterator::atEnd() const
{
if(!obj) return true;
return false;
}
const QChar &BidiIterator::current() const
{
static QChar nullCharacter;
if (!obj || !obj->isText())
return nullCharacter;
RenderText* text = static_cast<RenderText*>(obj);
if (!text->text())
return nullCharacter;
return text->text()[pos];
}
inline QChar::Direction BidiIterator::direction() const
{
if (!obj)
return QChar::DirON;
if (obj->isListMarker())
return obj->style()->direction() == LTR ? QChar::DirL : QChar::DirR;
if (!obj->isText())
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -