WCHAR *EbookEngine::ExtractPageText(int pageNo, WCHAR *lineSep, RectI **coords_out, RenderTarget target) { ScopedCritSec scope(&pagesAccess); str::Str<WCHAR> content; Vec<RectI> coords; bool insertSpace = false; Vec<DrawInstr> *pageInstrs = GetHtmlPage(pageNo); for (DrawInstr *i = pageInstrs->IterStart(); i; i = pageInstrs->IterNext()) { RectI bbox = GetInstrBbox(i, pageBorder); switch (i->type) { case InstrString: if (coords.Count() > 0 && bbox.x < coords.Last().BR().x) { content.Append(lineSep); coords.AppendBlanks(str::Len(lineSep)); CrashIf(*lineSep && !coords.Last().IsEmpty()); } else if (insertSpace && coords.Count() > 0) { int swidth = bbox.x - coords.Last().BR().x; if (swidth > 0) { content.Append(' '); coords.Append(RectI(bbox.x - swidth, bbox.y, swidth, bbox.dy)); } } insertSpace = false; { ScopedMem<WCHAR> s(str::conv::FromHtmlUtf8(i->str.s, i->str.len)); content.Append(s); size_t len = str::Len(s); double cwidth = 1.0 * bbox.dx / len; for (size_t k = 0; k < len; k++) coords.Append(RectI((int)(bbox.x + k * cwidth), bbox.y, (int)cwidth, bbox.dy)); } break; case InstrRtlString: if (coords.Count() > 0 && bbox.BR().x > coords.Last().x) { content.Append(lineSep); coords.AppendBlanks(str::Len(lineSep)); CrashIf(*lineSep && !coords.Last().IsEmpty()); } else if (insertSpace && coords.Count() > 0) { int swidth = coords.Last().x - bbox.BR().x; if (swidth > 0) { content.Append(' '); coords.Append(RectI(bbox.BR().x, bbox.y, swidth, bbox.dy)); } } insertSpace = false; { ScopedMem<WCHAR> s(str::conv::FromHtmlUtf8(i->str.s, i->str.len)); content.Append(s); size_t len = str::Len(s); double cwidth = 1.0 * bbox.dx / len; for (size_t k = 0; k < len; k++) coords.Append(RectI((int)(bbox.x + (len - k - 1) * cwidth), bbox.y, (int)cwidth, bbox.dy)); } break; case InstrElasticSpace: case InstrFixedSpace: insertSpace = true; break; } } if (coords_out) { CrashIf(coords.Count() != content.Count()); *coords_out = new RectI[coords.Count()]; memcpy(*coords_out, coords.LendData(), coords.Count() * sizeof(RectI)); } return content.StealData(); }