// Paint windows in z-order by first collecting the windows // and then painting consecutive layers with the same z-order, // starting with the lowest z-order. // We don't sort because we want to preserve the order of // containment of windows with the same z-order and non-stable // sort could change it. static void PaintWindowsInZOrder(Graphics *g, Control *c) { Vec<CtrlAndOffset> toPaint; WndFilter wndFilter; CtrlAndOffset * coff; Pen debugPen(Color(255, 0, 0), 1); CollectWindowsBreathFirst(c, 0, 0, &wndFilter, &toPaint); size_t paintedCount = 0; int16 lastPaintedZOrder = INT16_MIN; for (;;) { // find which z-order should we paint now int16 minUnpaintedZOrder = INT16_MAX; for (coff = toPaint.IterStart(); coff; coff = toPaint.IterNext()) { int16 zOrder = coff->c->zOrder; if ((zOrder > lastPaintedZOrder) && (zOrder < minUnpaintedZOrder)) minUnpaintedZOrder = zOrder; } for (coff = toPaint.IterStart(); coff; coff = toPaint.IterNext()) { if (minUnpaintedZOrder == coff->c->zOrder) { coff->c->Paint(g, coff->offX, coff->offY); if (IsDebugPaint()) { Rect bbox(coff->offX, coff->offY, coff->c->pos.Width, coff->c->pos.Height); g->DrawRectangle(&debugPen, bbox); } ++paintedCount; } } if (paintedCount == toPaint.Count()) return; CrashIf(paintedCount > toPaint.Count()); lastPaintedZOrder = minUnpaintedZOrder; } }
// TODO: if elements are of different sizes (e.g. texts using different fonts) // we should align them according to the baseline (which we would first need to // record for each element) static void SetYPos(Vec<DrawInstr>& instr, float y) { for (DrawInstr *i = instr.IterStart(); i; i = instr.IterNext()) { if (IsVisibleDrawInstr(i)) i->bbox.Y = y; } }
PageDestination *MobiEngineImpl::GetNamedDest(const WCHAR *name) { int filePos = _wtoi(name); if (filePos < 0 || 0 == filePos && *name != '0') return NULL; int pageNo; for (pageNo = 1; pageNo < PageCount(); pageNo++) { if (pages->At(pageNo)->reparseIdx > filePos) break; } CrashIf(pageNo < 1 || pageNo > PageCount()); size_t htmlLen; char *start = doc->GetBookHtmlData(htmlLen); if ((size_t)filePos > htmlLen) return NULL; ScopedCritSec scope(&pagesAccess); Vec<DrawInstr> *pageInstrs = GetHtmlPage(pageNo); // link to the bottom of the page, if filePos points // beyond the last visible DrawInstr of a page float currY = (float)pageRect.dy; for (DrawInstr *i = pageInstrs->IterStart(); i; i = pageInstrs->IterNext()) { if ((InstrString == i->type || InstrRtlString == i->type) && i->str.s >= start && i->str.s <= start + htmlLen && i->str.s - start >= filePos) { currY = i->bbox.Y; break; } } RectD rect(0, currY + pageBorder, pageRect.dx, 10); rect.Inflate(-pageBorder, 0); return new SimpleDest2(pageNo, rect); }
void EbookEngine::FixFontSizeForResolution(HDC hDC) { int dpi = GetDeviceCaps(hDC, LOGPIXELSY); if (dpi == currFontDpi) return; ScopedCritSec scope(&pagesAccess); float dpiFactor = 1.0f * currFontDpi / dpi; Graphics g(hDC); LOGFONTW lfw; for (int pageNo = 1; pageNo <= PageCount(); pageNo++) { Vec<DrawInstr> *pageInstrs = GetHtmlPage(pageNo); for (DrawInstr *i = pageInstrs->IterStart(); i; i = pageInstrs->IterNext()) { if (InstrSetFont == i->type) { Status ok = i->font->GetLogFontW(&g, &lfw); if (Ok == ok) { REAL newSize = i->font->GetSize() * dpiFactor; FontStyle newStyle = (FontStyle)i->font->GetStyle(); i->font = mui::GetCachedFont(lfw.lfFaceName, newSize, newStyle); } } } } currFontDpi = dpi; }
bool CbxEngineImpl::FinishLoadingCbz() { fileExt = L".cbz"; Vec<const WCHAR *> allFileNames; for (size_t idx = 0; idx < cbzFile->GetFileCount(); idx++) { const WCHAR *fileName = cbzFile->GetFileName(idx); // bail, if we accidentally try to load an XPS file if (fileName && str::StartsWith(fileName, L"_rels/.rels")) return false; if (fileName && ImageEngine::IsSupportedFile(fileName) && // OS X occasionally leaves metadata with image extensions !str::StartsWith(path::GetBaseName(fileName), L".")) { allFileNames.Append(fileName); } else { allFileNames.Append(NULL); } } assert(allFileNames.Count() == cbzFile->GetFileCount()); ScopedMem<char> metadata(cbzFile->GetFileData(L"ComicInfo.xml")); if (metadata) ParseComicInfoXml(metadata); metadata.Set(cbzFile->GetComment()); if (metadata) json::Parse(metadata, this); Vec<const WCHAR *> pageFileNames; for (const WCHAR **fn = allFileNames.IterStart(); fn; fn = allFileNames.IterNext()) { if (*fn) pageFileNames.Append(*fn); } pageFileNames.Sort(cmpAscii); for (const WCHAR **fn = pageFileNames.IterStart(); fn; fn = pageFileNames.IterNext()) { fileIdxs.Append(allFileNames.Find(*fn)); } assert(pageFileNames.Count() == fileIdxs.Count()); if (fileIdxs.Count() == 0) return false; pages.AppendBlanks(fileIdxs.Count()); mediaboxes.AppendBlanks(fileIdxs.Count()); return true; }
void VerticalLayout::Arrange(const Rect finalRect) { DirectionalLayoutData * e; SizeInfo * si; Vec<SizeInfo> sizes; for (e = els.IterStart(); e; e = els.IterNext()) { SizeInfo sizeInfo = { e->desiredSize.Height, e->sizeLayoutAxis, 0, 0 }; sizes.Append(sizeInfo); } RedistributeSizes(sizes.LendData(), sizes.Count(), finalRect.Height); for (e = els.IterStart(), si = sizes.IterStart(); e; e = els.IterNext(), si = sizes.IterNext()) { int dx = CalcScaledClippedSize(finalRect.Width, e->sizeNonLayoutAxis, e->desiredSize.Width); int x = e->alignNonLayoutAxis.CalcOffset(dx, finalRect.Width); e->element->Arrange(Rect(x, si->finalPos, dx, si->finalSize)); } }
Font *GetFont(const WCHAR *name, float size, FontStyle style) { Entry f = { (WCHAR *)name, size, style, NULL }; for (Entry *e = cache.IterStart(); e; e = cache.IterNext()) { if (f == *e) return e->font; } f.font = ::new Font(name, size, style); if (!f.font) { // fall back to the default font, if a desired font can't be created f.font = ::new Font(L"Times New Roman", size, style); if (!f.font) { if (cache.Count() > 0) return cache.At(0).font; return NULL; } } f.name = str::Dup(f.name); cache.Append(f); return f.font; }
static void VecTest() { Vec<int> ints; assert(ints.Count() == 0); ints.Append(1); ints.Push(2); ints.InsertAt(0, -1); assert(ints.Count() == 3); assert(ints.At(0) == -1 && ints.At(1) == 1 && ints.At(2) == 2); assert(ints.At(0) == -1 && ints.Last() == 2); int last = ints.Pop(); assert(last == 2); assert(ints.Count() == 2); ints.Push(3); ints.RemoveAt(0); assert(ints.Count() == 2); assert(ints.At(0) == 1 && ints.At(1) == 3); ints.Reset(); assert(ints.Count() == 0); for (int i = 0; i < 1000; i++) { ints.Push(i); } assert(ints.Count() == 1000 && ints.At(500) == 500); ints.Remove(500); assert(ints.Count() == 999 && ints.At(500) == 501); last = ints.Pop(); assert(last == 999); ints.Append(last); assert(ints.AtPtr(501) == &ints.At(501)); { Vec<int> ints2(ints); assert(ints2.Count() == 999); assert(ints.LendData() != ints2.LendData()); ints.Remove(600); assert(ints.Count() < ints2.Count()); ints2 = ints; assert(ints2.Count() == 998); } { char buf[2] = {'a', '\0'}; str::Str<char> v(0); for (int i = 0; i < 7; i++) { v.Append(buf, 1); buf[0] = buf[0] + 1; } char *s = v.LendData(); assert(str::Eq("abcdefg", s)); assert(7 == v.Count()); v.Set("helo"); assert(4 == v.Count()); assert(str::Eq("helo", v.LendData())); } { str::Str<char> v(128); v.Append("boo", 3); assert(str::Eq("boo", v.LendData())); assert(v.Count() == 3); v.Append("fop"); assert(str::Eq("boofop", v.LendData())); assert(v.Count() == 6); v.RemoveAt(2, 3); assert(v.Count() == 3); assert(str::Eq("bop", v.LendData())); v.Append('a'); assert(v.Count() == 4); assert(str::Eq("bopa", v.LendData())); char *s = v.StealData(); assert(str::Eq("bopa", s)); free(s); assert(v.Count() == 0); } { str::Str<char> v(0); for (int i = 0; i < 32; i++) { assert(v.Count() == i * 6); v.Append("lambd", 5); if (i % 2 == 0) v.Append('a'); else v.Push('a'); } for (int i=1; i<=16; i++) { v.RemoveAt((16 - i) * 6, 6); assert(v.Count() == (32 - i) * 6); } v.RemoveAt(0, 6 * 15); assert(v.Count() == 6); char *s = v.LendData(); assert(str::Eq(s, "lambda")); s = v.StealData(); assert(str::Eq(s, "lambda")); free(s); assert(v.Count() == 0); v.Append("lambda"); assert(str::Eq(v.LendData(), "lambda")); char c = v.Pop(); assert(c == 'a'); assert(str::Eq(v.LendData(), "lambd")); } VecTestAppendFmt(); { Vec<PointI *> v; srand((unsigned int)time(NULL)); for (int i = 0; i < 128; i++) { v.Append(new PointI(i, i)); size_t pos = rand() % v.Count(); v.InsertAt(pos, new PointI(i, i)); } assert(v.Count() == 128 * 2); size_t idx = 0; for (PointI **p = v.IterStart(); p; p = v.IterNext()) { assert(idx == v.IterIdx()); ++idx; } while (v.Count() > 64) { size_t pos = rand() % v.Count(); PointI *f = v.At(pos); v.Remove(f); delete f; } DeleteVecMembers(v); } { Vec<int> v; v.Append(2); for (int i = 0; i < 500; i++) v.Append(4); v.At(250) = 5; v.Reverse(); assert(v.Count() == 501 && v.At(0) == 4 && v.At(249) == v.At(251) && v.At(250) == 5 && v.At(500) == 2); v.Remove(4); v.Reverse(); assert(v.Count() == 500 && v.At(0) == 2 && v.At(249) == v.At(251) && v.At(250) == 5 && v.At(499) == 4); } }
~FontCache() { for (Entry *e = cache.IterStart(); e; e = cache.IterNext()) { free(e->name); ::delete e->font; } }
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(); }