summaryrefslogtreecommitdiff
path: root/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc')
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc569
1 files changed, 432 insertions, 137 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
index e2da940bb41..86d08414476 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
@@ -8,10 +8,6 @@
#include <aconf.h>
-#ifdef USE_GCC_PRAGMAS
-#pragma implementation
-#endif
-
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
@@ -65,7 +61,11 @@
// A large character has a font size larger than
// largeCharThreshold * avgFontSize.
-#define largeCharThreshold 1.5
+#define largeCharThreshold 2.0
+
+// Max ratio of large-to-total chars to prefer a large char split over
+// a line split.
+#define largeCharRatio 0.2
// A block will be split vertically only if the resulting chunk widths
// are greater than minChunkWidth * avgFontSize. However, Blocks of
@@ -83,9 +83,9 @@
#define maxWordGapSize 2.5
#define tableModeMaxWordGapSize 1.1
-// Adjacent lines can overlap by at most lineOverlapThreshold *
-// lineHeight.
-#define lineOverlapThreshold 0.33
+// Chars whose baseline is within baselineRange * avgFontSize of a
+// line's current average baseline are added to the line.
+#define baselineRange 0.5
// Subscripts (superscripts) must overlap the next (previous) line by
// minSubSuperscriptVertOverlap * fontSize.
@@ -214,12 +214,14 @@ public:
static int cmpX(const void *p1, const void *p2);
static int cmpY(const void *p1, const void *p2);
+ static int cmpBase(const void *p1, const void *p2);
static int cmpCharPos(const void *p1, const void *p2);
Unicode c;
int charPos;
int charLen;
double xMin, yMin, xMax, yMax;
+ double base;
TextFontInfo *font;
double fontSize;
double colorR,
@@ -249,38 +251,50 @@ TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
yMin = yMinA;
xMax = xMaxA;
yMax = yMaxA;
- // this can happen with vertical writing mode, or with odd values
- // for the char/word spacing parameters
- if (xMin > xMax) {
- t = xMin; xMin = xMax; xMax = t;
- }
- if (yMin > yMax) {
- t = yMin; yMin = yMax; yMax = t;
+ switch (rotA) {
+ case 0:
+ default: base = yMax + fontSizeA * fontA->getDescent(); break;
+ case 1: base = xMin - fontSizeA * fontA->getDescent(); break;
+ case 2: base = yMin - fontSizeA * fontA->getDescent(); break;
+ case 3: base = xMax + fontSizeA * fontA->getDescent(); break;
}
// TextPage::findGaps uses integer coordinates, so clip the char
// bbox to fit in a 32-bit int (this is generally only a problem in
// damaged PDF files)
if (xMin < -1e8) {
xMin = -1e8;
+ } else if (xMin > 1e8) {
+ xMin = 1e8;
}
- if (xMax > 1e8) {
+ if (xMax < -1e8) {
+ xMax = -1e8;
+ } else if (xMax > 1e8) {
xMax = 1e8;
}
if (yMin < -1e8) {
yMin = -1e8;
+ } else if (yMin > 1e8) {
+ yMin = 1e8;
}
- if (yMax > 1e8) {
+ if (yMax < -1e8) {
+ yMax = -1e8;
+ } else if (yMax > 1e8) {
yMax = 1e8;
}
- // zero-width characters will cause problems in the splitting code
- if (rotA & 1) {
- if (yMax - yMin < 1e-6) {
- yMax = yMin + 1e-6;
- }
- } else {
- if (xMax - xMin < 1e-6) {
- xMax = xMin + 1e-6;
- }
+ // this can happen with vertical writing mode, or with odd values
+ // for the char/word spacing parameters
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ // zero-width/height characters will cause problems in the splitting code
+ if (xMax - xMin < 1e-6) {
+ xMax = xMin + 1e-6;
+ }
+ if (yMax - yMin < 1e-6) {
+ yMax = yMin + 1e-6;
}
rot = (Guchar)rotA;
rotated = (char)rotatedA;
@@ -321,6 +335,19 @@ int TextChar::cmpY(const void *p1, const void *p2) {
}
}
+int TextChar::cmpBase(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->base < ch2->base) {
+ return -1;
+ } else if (ch1->base > ch2->base) {
+ return 1;
+ } else {
+ return ch1->charPos - ch2->charPos;
+ }
+}
+
int TextChar::cmpCharPos(const void *p1, const void *p2) {
const TextChar *ch1 = *(const TextChar **)p1;
const TextChar *ch2 = *(const TextChar **)p2;
@@ -985,7 +1012,8 @@ TextLine::TextLine(GList *wordsA, double xMinA, double yMinA,
}
}
//~ need to check for other Unicode chars used as hyphens
- hyphenated = text[len - 1] == (Unicode)'-';
+ hyphenated = text[len - 1] == (Unicode)'-' ||
+ text[len - 1] == (Unicode)0xad;
}
TextLine::~TextLine() {
@@ -1212,6 +1240,7 @@ TextPage::TextPage(TextOutputControl *controlA) {
chars = new GList();
fonts = new GList();
+ primaryRot = 0;
underlines = new GList();
links = new GList();
@@ -1240,6 +1269,15 @@ void TextPage::startPage(GfxState *state) {
if (state) {
pageWidth = state->getPageWidth();
pageHeight = state->getPageHeight();
+ // When rotating characters, the coordinates are flipped using the
+ // page size. See the comment in TextChar::TextChar() about
+ // integer coordinates.
+ if (pageWidth > 1e8) {
+ pageWidth = 1e8;
+ }
+ if (pageHeight > 1e8) {
+ pageHeight = 1e8;
+ }
} else {
pageWidth = pageHeight = 0;
}
@@ -1266,6 +1304,9 @@ void TextPage::clear() {
underlines = new GList();
deleteGList(links, TextLink);
links = new GList();
+ nVisibleChars = 0;
+ nInvisibleChars = 0;
+ nRemovedDupChars = 0;
if (findCols) {
deleteGList(findCols, TextColumn);
@@ -1560,13 +1601,18 @@ void TextPage::addChar(GfxState *state, double x, double y,
} else {
j = i;
}
+ GBool invisible = state->getRender() == 3 || alpha < 0.001;
chars->append(new TextChar(uBuf[j], charPos, nBytes,
xMin, yMin, xMax, yMax,
- curRot, rotated, clipped,
- state->getRender() == 3 || alpha < 0.001,
+ curRot, rotated, clipped, invisible,
curFont, curFontSize,
colToDbl(rgb.r), colToDbl(rgb.g),
colToDbl(rgb.b)));
+ if (invisible) {
+ ++nInvisibleChars;
+ } else {
+ ++nVisibleChars;
+ }
}
}
@@ -1730,7 +1776,7 @@ void TextPage::writeReadingOrder(void *outputStream,
GList *columns;
GBool primaryLR;
GString *s;
- int colIdx, parIdx, lineIdx, rot, n;
+ int colIdx, parIdx, lineIdx, n;
#if 0 //~debug
dumpChars(chars);
@@ -1740,7 +1786,7 @@ void TextPage::writeReadingOrder(void *outputStream,
} else {
overlappingChars = NULL;
}
- rot = rotateChars(chars);
+ primaryRot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
tree = splitChars(chars);
#if 0 //~debug
@@ -1748,14 +1794,14 @@ void TextPage::writeReadingOrder(void *outputStream,
#endif
if (!tree) {
// no text
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
return;
}
columns = buildColumns(tree, primaryLR);
delete tree;
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
if (control.html) {
- rotateUnderlinesAndLinks(rot);
+ rotateUnderlinesAndLinks(primaryRot);
generateUnderlinesAndLinks(columns);
}
if (overlappingChars) {
@@ -1799,10 +1845,10 @@ GList *TextPage::makeColumns() {
GList *overlappingChars;
GList *columns;
GBool primaryLR;
- int rot;
if (control.mode == textOutSimple2Layout) {
primaryLR = checkPrimaryLR(chars);
+ primaryRot = 0;
rotateCharsToZero(chars);
columns = buildSimple2Columns(chars);
unrotateCharsFromZero(chars);
@@ -1813,7 +1859,7 @@ GList *TextPage::makeColumns() {
} else {
overlappingChars = NULL;
}
- rot = rotateChars(chars);
+ primaryRot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
if ((tree = splitChars(chars))) {
columns = buildColumns(tree, primaryLR);
@@ -1822,8 +1868,8 @@ GList *TextPage::makeColumns() {
// no text
columns = new GList();
}
- unrotateChars(chars, rot);
- unrotateColumns(columns, rot);
+ unrotateChars(chars, primaryRot);
+ unrotateColumns(columns, primaryRot);
if (control.html) {
generateUnderlinesAndLinks(columns);
}
@@ -1852,7 +1898,7 @@ void TextPage::writePhysLayout(void *outputStream,
GList *overlappingChars;
GList *columns;
GBool primaryLR;
- int ph, colIdx, parIdx, lineIdx, rot, y, i;
+ int ph, colIdx, parIdx, lineIdx, y, i;
#if 0 //~debug
dumpChars(chars);
@@ -1865,7 +1911,7 @@ void TextPage::writePhysLayout(void *outputStream,
} else {
overlappingChars = NULL;
}
- rot = rotateChars(chars);
+ primaryRot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
tree = splitChars(chars);
#if 0 //~debug
@@ -1873,15 +1919,15 @@ void TextPage::writePhysLayout(void *outputStream,
#endif
if (!tree) {
// no text
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
return;
}
//~ this doesn't correctly handle the right-to-left case
columns = buildColumns(tree, gTrue);
delete tree;
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
if (control.html) {
- rotateUnderlinesAndLinks(rot);
+ rotateUnderlinesAndLinks(primaryRot);
generateUnderlinesAndLinks(columns);
}
ph = assignPhysLayoutPositions(columns);
@@ -1940,7 +1986,7 @@ void TextPage::writePhysLayout(void *outputStream,
if (overlappingChars) {
if (overlappingChars->getLength() > 0) {
- TextColumn *col = buildOverlappingTextColumn(overlappingChars);
+ col = buildOverlappingTextColumn(overlappingChars);
(*outputFunc)(outputStream, eol, eolLen);
for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
par = (TextParagraph *)col->paragraphs->get(parIdx);
@@ -1973,12 +2019,12 @@ void TextPage::writeSimpleLayout(void *outputStream,
GList *superLines;
GString *out;
GBool primaryLR;
- int rot, x, i, j;
+ int x, i, j;
#if 0 //~debug
dumpChars(chars);
#endif
- rot = rotateChars(chars);
+ primaryRot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
tree = splitChars(chars);
#if 0 //~debug
@@ -1986,13 +2032,13 @@ void TextPage::writeSimpleLayout(void *outputStream,
#endif
if (!tree) {
// no text
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
return;
}
superLines = new GList();
buildSuperLines(tree, superLines);
delete tree;
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
assignSimpleLayoutPositions(superLines, uMap);
for (i = 0; i < superLines->getLength(); ++i) {
@@ -2036,6 +2082,7 @@ void TextPage::writeSimple2Layout(void *outputStream,
int colIdx, parIdx, lineIdx;
primaryLR = checkPrimaryLR(chars);
+ primaryRot = 0;
rotateCharsToZero(chars);
#if 0 //~debug
dumpChars(chars);
@@ -2074,9 +2121,9 @@ void TextPage::writeLinePrinter(void *outputStream,
double pitch, lineSpacing, delta;
double yMin0, yShift, xMin0, xShift;
double y, x;
- int rot, n, i, j, k;
+ int n, i, j, k;
- rot = rotateChars(chars);
+ primaryRot = rotateChars(chars);
chars->sort(&TextChar::cmpX);
// don't call removeDuplicates here, because it expects to be
// working on a secondary list that doesn't own the TextChar objects
@@ -2186,7 +2233,7 @@ void TextPage::writeLinePrinter(void *outputStream,
delete line;
}
- unrotateChars(chars, rot);
+ unrotateChars(chars, primaryRot);
}
void TextPage::writeRaw(void *outputStream,
@@ -2451,6 +2498,7 @@ int TextPage::rotateChars(GList *charsA) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageWidth - ch->base;
ch->rot = (ch->rot + 3) & 3;
}
t = pageWidth;
@@ -2468,6 +2516,7 @@ int TextPage::rotateChars(GList *charsA) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageHeight - ch->base;
ch->rot = (ch->rot + 2) & 3;
}
break;
@@ -2515,6 +2564,7 @@ void TextPage::rotateCharsToZero(GList *charsA) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageWidth - ch->base;
break;
case 2:
xMin = pageWidth - ch->xMax;
@@ -2525,6 +2575,7 @@ void TextPage::rotateCharsToZero(GList *charsA) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageHeight - ch->base;
break;
case 3:
xMin = pageHeight - ch->yMax;
@@ -2654,6 +2705,7 @@ void TextPage::unrotateChars(GList *charsA, int rot) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageWidth - ch->base;
ch->rot = (ch->rot + 1) & 3;
}
break;
@@ -2668,6 +2720,7 @@ void TextPage::unrotateChars(GList *charsA, int rot) {
ch->xMax = xMax;
ch->yMin = yMin;
ch->yMax = yMax;
+ ch->base = pageHeight - ch->base;
ch->rot = (ch->rot + 2) & 3;
}
break;
@@ -3220,7 +3273,7 @@ GBool TextPage::checkPrimaryLR(GList *charsA) {
// by x for rot=0,2; by y for rot=1,3.
void TextPage::removeDuplicates(GList *charsA, int rot) {
TextChar *ch, *ch2;
- double xDelta, yDelta;
+ double xDelta, yDelta, xDelta2, yDelta2;
int i, j;
if (rot & 1) {
@@ -3229,6 +3282,10 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
ch = (TextChar *)charsA->get(i);
xDelta = dupMaxSecDelta * ch->fontSize;
yDelta = dupMaxPriDelta * ch->fontSize;
+ yDelta2 = 0.5 * (ch->yMax - ch->yMin);
+ if (yDelta2 < yDelta) {
+ yDelta = yDelta2;
+ }
j = i + 1;
while (j < charsA->getLength()) {
ch2 = (TextChar *)charsA->get(j);
@@ -3241,6 +3298,7 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
fabs(ch2->yMax - ch->yMax) < yDelta) {
if (ch->invisible && !ch2->invisible) {
charsA->del(i);
+ ++nRemovedDupChars;
--i;
break;
}
@@ -3248,6 +3306,7 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
ch->spaceAfter = (char)gTrue;
}
charsA->del(j);
+ ++nRemovedDupChars;
} else {
++j;
}
@@ -3259,6 +3318,10 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
while (i < charsA->getLength()) {
ch = (TextChar *)charsA->get(i);
xDelta = dupMaxPriDelta * ch->fontSize;
+ xDelta2 = 0.5 * (ch->xMax - ch->xMin);
+ if (xDelta2 < xDelta) {
+ xDelta = xDelta2;
+ }
yDelta = dupMaxSecDelta * ch->fontSize;
j = i + 1;
while (j < charsA->getLength()) {
@@ -3272,6 +3335,7 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
fabs(ch2->yMax - ch->yMax) < yDelta) {
if (ch->invisible && !ch2->invisible) {
charsA->del(i);
+ ++nRemovedDupChars;
--i;
break;
}
@@ -3279,6 +3343,7 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
ch->spaceAfter = (char)gTrue;
}
charsA->del(j);
+ ++nRemovedDupChars;
} else {
++j;
}
@@ -3559,7 +3624,7 @@ TextBlock *TextPage::splitChars(GList *charsA) {
}
}
if (chars2->getLength() > 0) {
- tree[rot] = split(chars2, rot, gFalse);
+ tree[rot] = split(chars2, rot);
}
}
delete chars2;
@@ -3590,9 +3655,11 @@ TextBlock *TextPage::splitChars(GList *charsA) {
}
// merge non-primary-rotation text into the primary-rotation tree
+ GBool doReorder = control.mode == textOutReadingOrder &&
+ globalParams->getSeparateRotatedText();
for (rot = 1; rot < 4; ++rot) {
if (tree[rot]) {
- insertIntoTree(tree[rot], tree[0]);
+ insertIntoTree(tree[rot], tree[0], doReorder);
tree[rot] = NULL;
}
}
@@ -3610,7 +3677,7 @@ TextBlock *TextPage::splitChars(GList *charsA) {
}
// Generate a tree of TextBlocks, marked as columns, lines, and words.
-TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
+TextBlock *TextPage::split(GList *charsA, int rot) {
TextBlock *blk;
GList *chars2, *chars3;
GList *splitLines;
@@ -3762,18 +3829,13 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
doHorizSplit = doVertSplit = doLineSplit = doLargeCharSplit = gFalse;
smallSplit = gFalse;
if (rot & 1) {
- if (vertOnly) {
- if (nHorizGaps > 0 && horizGapSize > minGapSize * minFontSize) {
- doHorizSplit = gTrue;
- smallSplit = horizGapSize < smallSplitThreshold;
- }
- } else if (control.mode == textOutSimpleLayout) {
+ if (control.mode == textOutSimpleLayout) {
if (nVertGaps > 0) {
doVertSplit = gTrue;
} else if (nHorizGaps > 0 && horizGapSize > minGapSize * minFontSize) {
doHorizSplit = gTrue;
smallSplit = horizGapSize < smallSplitThreshold;
- } else if (!vertOnly && splitLines->getLength() > 1) {
+ } else if (splitLines->getLength() > 1) {
doLineSplit = gTrue;
}
} else if (nHorizGaps > 0 &&
@@ -3786,19 +3848,15 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
smallSplit = horizGapSize < smallSplitThreshold;
} else if (nVertGaps > 0) {
doVertSplit = gTrue;
- } else if (nLargeChars > 0) {
+ } else if (nLargeChars > 0 &&
+ ((nLargeChars < largeCharRatio * charsA->getLength()) ||
+ splitLines->getLength() <= 1)) {
doLargeCharSplit = gTrue;
} else if (splitLines->getLength() > 1) {
doLineSplit = gTrue;
}
} else {
- if (vertOnly) {
- if (nVertGaps > 0 &&
- vertGapSize > minGapSize * minFontSize) {
- doVertSplit = gTrue;
- smallSplit = vertGapSize < smallSplitThreshold;
- }
- } else if (control.mode == textOutSimpleLayout) {
+ if (control.mode == textOutSimpleLayout) {
if (nHorizGaps > 0) {
doHorizSplit = gTrue;
} else if (nVertGaps > 0 && vertGapSize > minGapSize * minFontSize) {
@@ -3817,7 +3875,9 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
smallSplit = vertGapSize < smallSplitThreshold;
} else if (nHorizGaps > 0) {
doHorizSplit = gTrue;
- } else if (nLargeChars > 0) {
+ } else if (nLargeChars > 0 &&
+ ((nLargeChars < largeCharRatio * charsA->getLength()) ||
+ splitLines->getLength() <= 1)) {
doLargeCharSplit = gTrue;
} else if (splitLines->getLength() > 1) {
doLineSplit = gTrue;
@@ -3847,13 +3907,13 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
if (vertGaps->getW(i) > vertGapSize - splitGapSlack * avgFontSize) {
x1 = vertGaps->getX(i);
chars2 = getChars(charsA, x0, yMin - 1, x1, yMax + 1);
- blk->addChild(split(chars2, rot, vertOnly));
+ blk->addChild(split(chars2, rot));
delete chars2;
x0 = x1;
}
}
chars2 = getChars(charsA, x0, yMin - 1, xMax + 1, yMax + 1);
- blk->addChild(split(chars2, rot, vertOnly));
+ blk->addChild(split(chars2, rot));
delete chars2;
// split horizontally
@@ -3874,13 +3934,13 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
if (horizGaps->getW(i) > horizGapSize - splitGapSlack * avgFontSize) {
y1 = horizGaps->getX(i);
chars2 = getChars(charsA, xMin - 1, y0, xMax + 1, y1);
- blk->addChild(split(chars2, rot, gFalse));
+ blk->addChild(split(chars2, rot));
delete chars2;
y0 = y1;
}
}
chars2 = getChars(charsA, xMin - 1, y0, xMax + 1, yMax + 1);
- blk->addChild(split(chars2, rot, gFalse));
+ blk->addChild(split(chars2, rot));
delete chars2;
// split into larger and smaller chars
@@ -3899,7 +3959,7 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
chars3->append(ch);
}
}
- blk = split(chars3, rot, gFalse);
+ blk = split(chars3, rot);
chars2->sort((rot & 1) ? &TextChar::cmpY : &TextChar::cmpX);
insertLargeChars(chars2, blk);
delete chars2;
@@ -3925,12 +3985,21 @@ TextBlock *TextPage::split(GList *charsA, int rot, GBool vertOnly) {
for (i = 0; i < splitLine->chars->getLength(); ++i) {
blk->addChild((TextChar *)splitLine->chars->get(i), gTrue);
}
+#if 0 //~debug
+ printf("line-leaf xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ xMin, pageHeight - yMax, xMax, pageHeight - yMin);
+ printf(" ");
+ for (i = 0; i < splitLine->chars->getLength(); ++i) {
+ printf("%c", ((TextChar *)splitLine->chars->get(i))->c);
+ }
+ printf("\n");
+#endif
} else {
blk = new TextBlock((rot & 1) ? blkVertSplit : blkHorizSplit, rot);
blk->smallSplit = gFalse;
for (i = 0; i < splitLines->getLength(); ++i) {
SplitLine *splitLine = (SplitLine *)splitLines->get(i);
- blk->addChild(split(splitLine->chars, rot, singleLine));
+ blk->addChild(split(splitLine->chars, rot));
}
}
@@ -4058,54 +4127,146 @@ void TextPage::findGaps(GList *charsA, int rot,
}
}
- //----- partition into lines and find horizontal gaps
+ //----- find horizontal gaps
if (rot & 1) {
charsA->sort(&TextChar::cmpX);
- SplitLine *splitLine = NULL;
- double xxMin = 0, xxMax = 0;
+ double xxMax = 0;
for (int i = 0; i < charsA->getLength(); ++i) {
TextChar *ch = (TextChar *)charsA->get(i);
- if (i == 0 ||
- ch->xMin > xxMax - lineOverlapThreshold * (xxMax - xxMin)) {
- if (i > 0 && ch->xMin > xxMax) {
- vertGaps->addGap(0.5 * (ch->xMin + xxMax), ch->xMin - xxMax);
- }
- xxMin = ch->xMin;
+ if (i == 0) {
xxMax = ch->xMax;
- splitLine = new SplitLine(i, i, xxMin, xxMax);
- splitLines->append(splitLine);
- } else {
- splitLine->lastCharIdx = i;
+ } else if (ch->xMin <= xxMax) {
if (ch->xMax > xxMax) {
xxMax = ch->xMax;
- splitLine->yMax = xxMax;
}
+ } else {
+ vertGaps->addGap(0.5 * (ch->xMin + xxMax), ch->xMin - xxMax);
+ xxMax = ch->xMax;
}
}
} else {
charsA->sort(&TextChar::cmpY);
- SplitLine *splitLine = NULL;
- double yyMin = 0, yyMax = 0;
+ double yyMax = 0;
for (int i = 0; i < charsA->getLength(); ++i) {
TextChar *ch = (TextChar *)charsA->get(i);
- if (i == 0 ||
- ch->yMin > yyMax - lineOverlapThreshold * (yyMax - yyMin)) {
- if (i > 0 && ch->yMin > yyMax) {
- horizGaps->addGap(0.5 * (ch->yMin + yyMax), ch->yMin - yyMax);
- }
- yyMin = ch->yMin;
+ if (i == 0) {
yyMax = ch->yMax;
- splitLine = new SplitLine(i, i, yyMin, yyMax);
- splitLines->append(splitLine);
+ } else if (ch->yMin <= yyMax) {
+ if (ch->yMax > yyMax) {
+ yyMax = ch->yMax;
+ }
} else {
- splitLine->lastCharIdx = i;
+ horizGaps->addGap(0.5 * (ch->yMin + yyMax), ch->yMin - yyMax);
+ yyMax = ch->yMax;
+ }
+ }
+ }
+
+ //----- split into lines
+
+ double baseDelta = baselineRange * avgFontSize;
+
+ if (rot & 1) {
+
+ // split into non-overlapping chunks
+ int firstChar = 0;
+ while (firstChar < charsA->getLength()) {
+ TextChar *ch = (TextChar *)charsA->get(firstChar);
+ double xxMax = ch->xMax;
+ int nextChar = firstChar + 1;
+ while (nextChar < charsA->getLength()) {
+ ch = (TextChar *)charsA->get(nextChar);
+ if (ch->xMin > xxMax) {
+ break;
+ }
+ if (ch->xMax > xxMax) {
+ xxMax = ch->xMax;
+ }
+ ++nextChar;
+ }
+
+ // split the chunk [firstChar, nextChar) into lines
+ charsA->sort(firstChar, nextChar - firstChar, &TextChar::cmpBase);
+ int i0 = firstChar;
+ while (i0 < nextChar) {
+ ch = (TextChar *)charsA->get(i0);
+ double baseTotal = ch->base;
+ double baseAvg = baseTotal;
+ double xxMin = ch->xMin;
+ xxMax = ch->xMax;
+ int i1 = i0 + 1;
+ while (i1 < nextChar) {
+ ch = (TextChar *)charsA->get(i1);
+ if (ch->base > baseAvg + baseDelta) {
+ break;
+ }
+ baseTotal += ch->base;
+ if (ch->xMin < xxMin) {
+ xxMin = ch->xMin;
+ }
+ if (ch->xMax > xxMax) {
+ xxMax = ch->xMax;
+ }
+ ++i1;
+ baseAvg = baseTotal / (i1 - i0);
+ }
+ splitLines->append(new SplitLine(i0, i1 - 1, xxMin, xxMax));
+ i0 = i1;
+ }
+ firstChar = nextChar;
+ }
+
+ } else {
+
+ // split into non-overlapping chunks
+ int firstChar = 0;
+ while (firstChar < charsA->getLength()) {
+ TextChar *ch = (TextChar *)charsA->get(firstChar);
+ double yyMax = ch->yMax;
+ int nextChar = firstChar + 1;
+ while (nextChar < charsA->getLength()) {
+ ch = (TextChar *)charsA->get(nextChar);
+ if (ch->yMin > yyMax) {
+ break;
+ }
if (ch->yMax > yyMax) {
yyMax = ch->yMax;
- splitLine->yMax = yyMax;
}
+ ++nextChar;
+ }
+
+ // split the chunk [firstChar, nextChar) into lines
+ charsA->sort(firstChar, nextChar - firstChar, &TextChar::cmpBase);
+ int i0 = firstChar;
+ while (i0 < nextChar) {
+ ch = (TextChar *)charsA->get(i0);
+ double baseTotal = ch->base;
+ double baseAvg = baseTotal;
+ double yyMin = ch->yMin;
+ yyMax = ch->yMax;
+ int i1 = i0 + 1;
+ while (i1 < nextChar) {
+ ch = (TextChar *)charsA->get(i1);
+ if (ch->base > baseAvg + baseDelta) {
+ break;
+ }
+ baseTotal += ch->base;
+ if (ch->yMin < yyMin) {
+ yyMin = ch->yMin;
+ }
+ if (ch->yMax > yyMax) {
+ yyMax = ch->yMax;
+ }
+ ++i1;
+ baseAvg = baseTotal / (i1 - i0);
+ }
+ splitLines->append(new SplitLine(i0, i1 - 1, yyMin, yyMax));
+ i0 = i1;
}
+ firstChar = nextChar;
}
+
}
}
@@ -4197,10 +4358,19 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
// merge sub/superscripts into correct lines
if (allSubSuper) {
+ idxAbove = 0;
+ idxBelow = 0;
for (int idx = 0; idx < splitLine->chars->getLength(); ++idx) {
TextChar *ch = (TextChar *)splitLine->chars->get(idx);
if (maybeSub && ch->xMin < prevLine->yMax) {
- prevLine->chars->append(ch);
+ while (idxAbove < prevLine->chars->getLength()) {
+ TextChar *ch2 = (TextChar *)prevLine->chars->get(idxAbove);
+ if (ch2->yMin > ch->yMin && ch2->xMin < ch->xMax && ch2->xMax > ch->xMin) {
+ break;
+ }
+ ++idxAbove;
+ }
+ prevLine->chars->insert(idxAbove, ch);
if (ch->xMin < prevLine->yMin) {
prevLine->yMin = ch->xMin;
}
@@ -4208,7 +4378,14 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
prevLine->yMax = ch->xMax;
}
} else {
- nextLine->chars->append(ch);
+ while (idxBelow < nextLine->chars->getLength()) {
+ TextChar *ch2 = (TextChar *)nextLine->chars->get(idxBelow);
+ if (ch2->yMin > ch->yMin && ch2->xMin < ch->xMax && ch2->xMax > ch->xMin) {
+ break;
+ }
+ ++idxBelow;
+ }
+ nextLine->chars->insert(idxBelow, ch);
if (ch->xMin < nextLine->yMin) {
nextLine->yMin = ch->xMin;
}
@@ -4217,12 +4394,6 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
}
}
}
- if (maybeSub) {
- prevLine->chars->sort(&TextChar::cmpY);
- }
- if (maybeSuper) {
- nextLine->chars->sort(&TextChar::cmpY);
- }
delete splitLine;
splitLines->del(i);
}
@@ -4252,14 +4423,14 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
prevLine = (SplitLine *)splitLines->get(i-1);
double minOverlap = minSubSuperscriptVertOverlap
* (prevLine->yMax - prevLine->yMin);
- maybeSub = prevLine->yMax - splitLine->yMin > minOverlap;
+ maybeSub = prevLine->yMax - splitLine->yMin > minOverlap;
}
GBool maybeSuper = gFalse;
if (i < splitLines->getLength() - 1) {
nextLine = (SplitLine *)splitLines->get(i+1);
double minOverlap = minSubSuperscriptVertOverlap
* (nextLine->yMax - nextLine->yMin);
- maybeSuper = splitLine->yMax - nextLine->yMin > minOverlap;
+ maybeSuper = splitLine->yMax - nextLine->yMin > minOverlap;
}
if (!maybeSub && !maybeSuper) {
continue;
@@ -4315,10 +4486,19 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
// merge sub/superscripts into correct lines
if (allSubSuper) {
+ idxAbove = 0;
+ idxBelow = 0;
for (int idx = 0; idx < splitLine->chars->getLength(); ++idx) {
TextChar *ch = (TextChar *)splitLine->chars->get(idx);
if (maybeSub && ch->yMin < prevLine->yMax) {
- prevLine->chars->append(ch);
+ while (idxAbove < prevLine->chars->getLength()) {
+ TextChar *ch2 = (TextChar *)prevLine->chars->get(idxAbove);
+ if (ch2->xMin > ch->xMin && ch2->yMin < ch->yMax && ch2->yMax > ch->yMin) {
+ break;
+ }
+ ++idxAbove;
+ }
+ prevLine->chars->insert(idxAbove, ch);
if (ch->yMin < prevLine->yMin) {
prevLine->yMin = ch->yMin;
}
@@ -4326,7 +4506,14 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
prevLine->yMax = ch->yMax;
}
} else {
- nextLine->chars->append(ch);
+ while (idxBelow < nextLine->chars->getLength()) {
+ TextChar *ch2 = (TextChar *)nextLine->chars->get(idxBelow);
+ if (ch2->xMin > ch->xMin && ch2->yMin < ch->yMax && ch2->yMax > ch->yMin) {
+ break;
+ }
+ ++idxBelow;
+ }
+ nextLine->chars->insert(idxBelow, ch);
if (ch->yMin < nextLine->yMin) {
nextLine->yMin = ch->yMin;
}
@@ -4335,12 +4522,6 @@ void TextPage::mergeSplitLines(GList *charsA, int rot, GList *splitLines) {
}
}
}
- if (maybeSub) {
- prevLine->chars->sort(&TextChar::cmpX);
- }
- if (maybeSuper) {
- nextLine->chars->sort(&TextChar::cmpX);
- }
delete splitLine;
splitLines->del(i);
}
@@ -4517,20 +4698,48 @@ void TextPage::insertLargeCharInLeaf(TextChar *ch, TextBlock *blk) {
}
// Merge blk (rot != 0) into primaryTree (rot == 0).
-void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) {
- TextBlock *child;
+void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree,
+ GBool doReorder) {
+ // if we're separating each rotation (only supported in reading
+ // order mode), reorder the block's children as appropriate and then
+ // append the block
+ if (doReorder) {
+ reorderBlocks(blk);
+ primaryTree->children->append(blk);
+
+ // otherwise, insert blocks into the primary tree in the appropriate
+ // locations
+ } else {
- // we insert a whole column at a time - so call insertIntoTree
- // recursively until we get to a column (or line)
+ // we insert a whole column at a time - so call insertIntoTree
+ // recursively until we get to a column (or line)
+ if (blk->tag == blkTagMulticolumn) {
+ while (blk->children->getLength()) {
+ TextBlock *child = (TextBlock *)blk->children->del(0);
+ insertIntoTree(child, primaryTree, doReorder);
+ }
+ delete blk;
- if (blk->tag == blkTagMulticolumn) {
- while (blk->children->getLength()) {
- child = (TextBlock *)blk->children->del(0);
- insertIntoTree(child, primaryTree);
+ } else {
+ insertColumnIntoTree(blk, primaryTree);
}
- delete blk;
- } else {
- insertColumnIntoTree(blk, primaryTree);
+ }
+}
+
+// Reorder non-primary-rotation blocks as appropriate (for reading
+// order mode only).
+void TextPage::reorderBlocks(TextBlock *blk) {
+ if (blk->tag != blkTagMulticolumn) {
+ return;
+ }
+ for (int i = 0; i < blk->children->getLength(); ++i) {
+ reorderBlocks((TextBlock *)blk->children->get(i));
+ }
+ //~ check/test these conditions
+ if ((blk->rot == 1 && blk->type == blkVertSplit) ||
+ blk->rot == 2 ||
+ (blk->rot == 3 && blk->type == blkHorizSplit)) {
+ blk->children->reverse();
}
}
@@ -5095,11 +5304,13 @@ TextLine *TextPage::buildLine(GList *charsA, int rot,
TextWord *word;
double wordSp, lineFontSize, sp;
int dir, dir2;
- GBool rotated, spaceAfter, spaceBefore;
+ GBool rotated2, spaceAfter, spaceBefore;
int i, j;
wordSp = computeWordSpacingThreshold(charsA, rot);
+ adjustCombiningChars(charsA, rot);
+
words = new GList();
lineFontSize = 0;
spaceBefore = gFalse;
@@ -5113,7 +5324,7 @@ TextLine *TextPage::buildLine(GList *charsA, int rot,
: (TextChar *)NULL,
(i < charsA->getLength() - 1) ? (TextChar *)charsA->get(i+1)
: (TextChar *)NULL);
- rotated = ((TextChar *)charsA->get(i))->rotated;
+ rotated2 = ((TextChar *)charsA->get(i))->rotated;
for (j = i+1; j < charsA->getLength(); ++j) {
ch = (TextChar *)charsA->get(j-1);
ch2 = (TextChar *)charsA->get(j);
@@ -5134,7 +5345,7 @@ TextLine *TextPage::buildLine(GList *charsA, int rot,
: (TextChar *)NULL);
if (ch->font != ch2->font ||
fabs(ch->fontSize - ch2->fontSize) > 0.01 ||
- (control.splitRotatedWords && ch2->rotated != rotated) ||
+ (control.splitRotatedWords && ch2->rotated != rotated2) ||
(dir && dir2 && dir2 != dir) ||
(control.mode == textOutRawOrder &&
ch2->charPos != ch->charPos + ch->charLen)) {
@@ -5145,7 +5356,7 @@ TextLine *TextPage::buildLine(GList *charsA, int rot,
}
sp = wordSp - 1;
}
- word = new TextWord(charsA, i, j - i, rot, rotated, dir,
+ word = new TextWord(charsA, i, j - i, rot, rotated2, dir,
(rot >= 2) ? spaceBefore : spaceAfter);
spaceBefore = spaceAfter;
i = j;
@@ -5277,6 +5488,89 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
}
}
+// Move Unicode combining characters around so that they come
+// immediately after the correct base characters.
+//~ this currently won't work for RtL scripts
+void TextPage::adjustCombiningChars(GList *charsA, int rot) {
+ int i = 1;
+ while (i < charsA->getLength()) {
+ TextChar *ch = (TextChar *)charsA->get(i);
+
+ // look for a zero-width combining char
+ if (!(unicodeTypeCombiningMark(ch->c) &&
+ fabs((rot & 1) ? ch->yMax - ch->yMin // width ~= zero
+ : ch->xMax - ch->xMin) < 0.01)) {
+ ++i;
+ continue;
+ }
+
+ // look for a sequence of zero-width combining chars at the same
+ // position (NB: slight negative char spacing can reorder things
+ // so that diacritics for different base chars appear next to each
+ // other)
+ int j;
+ for (j = i + 1; j < charsA->getLength(); ++j) {
+ TextChar *ch2 = (TextChar *)charsA->get(j);
+ if (!(unicodeTypeCombiningMark(ch2->c) &&
+ fabs((rot & 1) ? ch2->yMin - ch->yMax
+ : ch2->xMin - ch->xMax) < 0.01 * ch->fontSize &&
+ fabs((rot & 1) ? ch->yMax - ch->yMin // width ~= zero
+ : ch->xMax - ch->xMin) < 0.01)) {
+ break;
+ }
+ }
+
+ // identify the two previous chars
+ TextChar *prev1 = (i >= 2) ? (TextChar *)charsA->get(i - 2) : NULL;
+ TextChar *prev2 = (TextChar *)charsA->get(i - 1);
+
+ //~ this code currently makes things worse for RtL scripts, so
+ //~ skip any RtL text
+ if (unicodeTypeR(prev2->c)) {
+ ++i;
+ continue;
+ }
+
+ // if the combining chars are placed in the left 3/4 of prev2,
+ // assume the base char is prev1, not prev2, and reorder the chars
+ // accordingly
+ TextChar *base;
+ if (prev1 &&
+ ((rot & 1) ? (ch->yMin < 0.25 * prev2->yMin + 0.75 * prev2->yMax)
+ : (ch->xMin < 0.25 * prev2->xMin + 0.75 * prev2->xMax))) {
+ for (int k = i; k < j; ++k) {
+ charsA->put(k - 1, charsA->get(k));
+ }
+ charsA->put(j - 1, prev2);
+ --i;
+ --j;
+ base = prev1;
+ } else {
+ base = prev2;
+ }
+
+ // sort the diacritics into content stream order
+ // (slight negative char spacing, used for kerning, will push
+ // zero-width diacritics out of order)
+ if (j - i > 1) {
+ charsA->sort(i, j - i, &TextChar::cmpCharPos);
+ }
+
+ // set the diacritics' positions immediately to the right of the
+ // base char
+ for (int k = i; k < j; ++k) {
+ TextChar *ch2 = (TextChar *)charsA->get(k);
+ if (rot & 1) {
+ ch2->yMin = ch2->yMax = base->yMax;
+ } else {
+ ch2->xMin = ch2->xMax = base->xMax;
+ }
+ }
+
+ i = j;
+ }
+}
+
// Check the character's direction: returns 1 for L or Num; -1 for R; 0
// for others.
int TextPage::getCharDirection(TextChar *ch) {
@@ -6624,7 +6918,8 @@ void TextOutputDev::endString(GfxState *state) {
void TextOutputDev::drawChar(GfxState *state, double x, double y,
double dx, double dy,
double originX, double originY,
- CharCode c, int nBytes, Unicode *u, int uLen) {
+ CharCode c, int nBytes, Unicode *u, int uLen,
+ GBool fill, GBool stroke, GBool makePath) {
text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
}