Пример #1
0
	static int StringWidth(LPCWSTR s, HDC dc)
	{
		SIZE size;
		GetTextExtentPoint32W(dc, s, wcslen(s), &size);
		return size.cx;
	}
Пример #2
0
	static int CharWidth(wchar_t c, HDC dc)
	{
		SIZE size;
		GetTextExtentPoint32W(dc, &c, 1, &size);
		return size.cx;
	}
Пример #3
0
void
gfxGDIFont::Initialize()
{
    NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");

    LOGFONTW logFont;

    if (mAdjustedSize == 0.0) {
        mAdjustedSize = mStyle.size;
        if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) {
            // to implement font-size-adjust, we first create the "unadjusted" font
            FillLogFont(logFont, mAdjustedSize);
            mFont = ::CreateFontIndirectW(&logFont);

            // initialize its metrics so we can calculate size adjustment
            Initialize();

            // calculate the properly adjusted size, and then proceed
            // to recreate mFont and recalculate metrics
            gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
            mAdjustedSize = mStyle.GetAdjustedSize(aspect);

            // delete the temporary font and metrics
            ::DeleteObject(mFont);
            mFont = nsnull;
            delete mMetrics;
            mMetrics = nsnull;
        }
    }

    // this may end up being zero
    mAdjustedSize = ROUND(mAdjustedSize);
    FillLogFont(logFont, mAdjustedSize);
    mFont = ::CreateFontIndirectW(&logFont);

    mMetrics = new gfxFont::Metrics;
    ::memset(mMetrics, 0, sizeof(*mMetrics));

    AutoDC dc;
    SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
    AutoSelectFont selectFont(dc.GetDC(), mFont);

    // Get font metrics if size > 0
    if (mAdjustedSize > 0.0) {

        OUTLINETEXTMETRIC oMetrics;
        TEXTMETRIC& metrics = oMetrics.otmTextMetrics;

        if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
            mMetrics->superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y;
            // Some fonts have wrong sign on their subscript offset, bug 410917.
            mMetrics->subscriptOffset = fabs((double)oMetrics.otmptSubscriptOffset.y);
            mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
            mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
            mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
            mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;

            const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
            GLYPHMETRICS gm;
            DWORD len = GetGlyphOutlineW(dc.GetDC(), PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &kIdentityMatrix);
            if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
                // 56% of ascent, best guess for true type
                mMetrics->xHeight =
                    ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
            } else {
                mMetrics->xHeight = gm.gmptGlyphOrigin.y;
            }
            mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
            gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
            mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
            mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
            if (oMetrics.otmEMSquare > 0) {
                mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
            }
        } else {
            // Make a best-effort guess at extended metrics
            // this is based on general typographic guidelines
            
            // GetTextMetrics can fail if the font file has been removed
            // or corrupted recently.
            BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
            if (!result) {
                NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
                mIsValid = false;
                memset(mMetrics, 0, sizeof(*mMetrics));
                return;
            }

            mMetrics->xHeight =
                ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
            mMetrics->superscriptOffset = mMetrics->xHeight;
            mMetrics->subscriptOffset = mMetrics->xHeight;
            mMetrics->strikeoutSize = 1;
            mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
            mMetrics->underlineSize = 1;
            mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
            mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
            mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
            mMetrics->emDescent = metrics.tmDescent;
        }

        mMetrics->internalLeading = metrics.tmInternalLeading;
        mMetrics->externalLeading = metrics.tmExternalLeading;
        mMetrics->maxHeight = metrics.tmHeight;
        mMetrics->maxAscent = metrics.tmAscent;
        mMetrics->maxDescent = metrics.tmDescent;
        mMetrics->maxAdvance = metrics.tmMaxCharWidth;
        mMetrics->aveCharWidth = NS_MAX<gfxFloat>(1, metrics.tmAveCharWidth);
        // The font is monospace when TMPF_FIXED_PITCH is *not* set!
        // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
        if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
            mMetrics->maxAdvance = mMetrics->aveCharWidth;
        }

        // Cache the width of a single space.
        SIZE size;
        GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
        mMetrics->spaceWidth = ROUND(size.cx);

        // Cache the width of digit zero.
        // XXX MSDN (http://msdn.microsoft.com/en-us/library/ms534223.aspx)
        // does not say what the failure modes for GetTextExtentPoint32 are -
        // is it safe to assume it will fail iff the font has no '0'?
        if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size)) {
            mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
        } else {
            mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
        }

        mSpaceGlyph = 0;
        if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
            WORD glyph;
            DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
                                         GGI_MARK_NONEXISTING_GLYPHS);
            if (ret != GDI_ERROR && glyph != 0xFFFF) {
                mSpaceGlyph = glyph;
            }
        }

        SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
    }

    mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
                                                                mFont);

    cairo_matrix_t sizeMatrix, ctm;
    cairo_matrix_init_identity(&ctm);
    cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);

    cairo_font_options_t *fontOptions = cairo_font_options_create();
    if (mAntialiasOption != kAntialiasDefault) {
        cairo_font_options_set_antialias(fontOptions,
            GetCairoAntialiasOption(mAntialiasOption));
    }
    mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
                                           &ctm, fontOptions);
    cairo_font_options_destroy(fontOptions);

    if (!mScaledFont ||
        cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
#ifdef DEBUG
        char warnBuf[1024];
        sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
                mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
        NS_WARNING(warnBuf);
#endif
        mIsValid = false;
    } else {
        mIsValid = true;
    }

#if 0
    printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
           NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
    printf("    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
    printf("    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
    printf("    internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
    printf("    spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->xHeight);
    printf("    uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n",
           mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize,
           mMetrics->superscriptOffset, mMetrics->subscriptOffset);
#endif
}
Пример #4
0
	RenderedSubtitle* Renderer::Lookup(const Subtitle* s, const CSize& vs, const CRect& vr)
	{
		m_sra.UpdateTarget(vs, vr);

		if(s->m_text.IsEmpty()) {
			return NULL;
		}

		CRect spdrc = s->m_frame.reference == _T("video") ? vr : CRect(CPoint(0, 0), vs);

		if(spdrc.IsRectEmpty()) {
			return NULL;
		}

		RenderedSubtitle* rs = NULL;

		if(m_rsc.Lookup(s->m_name, rs)) {
			if(!s->m_animated && rs->m_spdrc == spdrc) {
				return rs;
			}

			m_rsc.Invalidate(s->m_name);
		}

		const Style& style = s->m_text.GetHead().style;

		Size scale;

		scale.cx = (float)spdrc.Width() / s->m_frame.resolution.cx;
		scale.cy = (float)spdrc.Height() / s->m_frame.resolution.cy;

		CRect frame;

		frame.left = (int)(64.0f * (spdrc.left + style.placement.margin.l * scale.cx) + 0.5);
		frame.top = (int)(64.0f * (spdrc.top + style.placement.margin.t * scale.cy) + 0.5);
		frame.right = (int)(64.0f * (spdrc.right - style.placement.margin.r * scale.cx) + 0.5);
		frame.bottom = (int)(64.0f * (spdrc.bottom - style.placement.margin.b * scale.cy) + 0.5);

		CRect clip;

		if(style.placement.clip.l == -1) {
			clip.left = 0;
		} else {
			clip.left = (int)(spdrc.left + style.placement.clip.l * scale.cx);
		}
		if(style.placement.clip.t == -1) {
			clip.top = 0;
		} else {
			clip.top = (int)(spdrc.top + style.placement.clip.t * scale.cy);
		}
		if(style.placement.clip.r == -1) {
			clip.right = vs.cx;
		} else {
			clip.right = (int)(spdrc.left + style.placement.clip.r * scale.cx);
		}
		if(style.placement.clip.b == -1) {
			clip.bottom = vs.cy;
		} else {
			clip.bottom = (int)(spdrc.top + style.placement.clip.b * scale.cy);
		}

		clip.left = max(clip.left, 0);
		clip.top = max(clip.top, 0);
		clip.right = min(clip.right, vs.cx);
		clip.bottom = min(clip.bottom, vs.cy);

		scale.cx *= 64;
		scale.cy *= 64;

		bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up");

		// create glyph paths

		WCHAR c_prev = 0, c_next;

		CAutoPtrList<Glyph> glyphs;

		POSITION pos = s->m_text.GetHeadPosition();
		while(pos) {
			const Text& t = s->m_text.GetNext(pos);

			LOGFONT lf;
			memset(&lf, 0, sizeof(lf));
			lf.lfCharSet = DEFAULT_CHARSET;
			_tcscpy_s(lf.lfFaceName, CString(t.style.font.face));
			lf.lfHeight = (LONG)(t.style.font.size * scale.cy + 0.5);
			lf.lfWeight = (LONG)(t.style.font.weight + 0.5);
			lf.lfItalic = !!t.style.font.italic;
			lf.lfUnderline = !!t.style.font.underline;
			lf.lfStrikeOut = !!t.style.font.strikethrough;
			lf.lfOutPrecision = OUT_TT_PRECIS;
			lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
			lf.lfQuality = ANTIALIASED_QUALITY;
			lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;

			FontWrapper* font = m_fc.Create(m_hDC, lf);

			if(!font) {
				_tcscpy_s(lf.lfFaceName, _T("Arial"));

				font = m_fc.Create(m_hDC, lf);
				if(!font) {
					ASSERT(0);
					continue;
				}
			}

			HFONT hOldFont = SelectFont(m_hDC, *font);

			const TEXTMETRIC& tm = font->GetTextMetric();

			for(LPCWSTR c = t.str; *c; c++) {
				CAutoPtr<Glyph> g(DNew Glyph());

				g->c = *c;
				g->style = t.style;
				g->scale = scale;
				g->vertical = vertical;
				g->font = font;

				c_next = !c[1] && pos ? c_next = s->m_text.GetAt(pos).str[0] : c[1];
				Arabic::Replace(g->c, c_prev, c_next);
				c_prev = c[0];

				CSize extent;
				GetTextExtentPoint32W(m_hDC, &g->c, 1, &extent);
				ASSERT(extent.cx >= 0 && extent.cy >= 0);

				if(vertical) {
					g->spacing = (int)(t.style.font.spacing * scale.cy + 0.5);
					g->ascent = extent.cx / 2;
					g->descent = extent.cx - g->ascent;
					g->width = extent.cy;

					// TESTME
					if(g->c == Text::SP) {
						g->width /= 2;
					}
				} else {
					g->spacing = (int)(t.style.font.spacing * scale.cx + 0.5);
					g->ascent = tm.tmAscent;
					g->descent = tm.tmDescent;
					g->width = extent.cx;
				}

				if(g->c == Text::LSEP) {
					g->spacing = 0;
					g->width = 0;
					g->ascent /= 2;
					g->descent /= 2;
				} else {
					GlyphPath* path = m_gpc.Create(m_hDC, font, g->c);
					if(!path) {
						ASSERT(0);
						continue;
					}
					g->path = *path;
				}

				glyphs.AddTail(g);
			}

			SelectFont(m_hDC, hOldFont);
		}

		// break glyphs into rows

		CAutoPtrList<Row> rows;
		CAutoPtr<Row> row;

		pos = glyphs.GetHeadPosition();
		while(pos) {
			CAutoPtr<Glyph> g = glyphs.GetNext(pos);
			if(!row) {
				row.Attach(DNew Row());
			}
			WCHAR c = g->c;
			row->AddTail(g);
			if(c == Text::LSEP || !pos) {
				rows.AddTail(row);
			}
		}

		// kerning

		if(s->m_direction.primary == _T("right")) { // || s->m_direction.primary == _T("left")
			for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos)) {
				Row* r = rows.GetAt(rpos);

				POSITION gpos = r->GetHeadPosition();
				while(gpos) {
					Glyph* g1 = r->GetNext(gpos);
					if(!gpos) {
						break;
					}

					Glyph* g2 = r->GetAt(gpos);
					if(g1->font != g2->font || !g1->style.font.kerning || !g2->style.font.kerning) {
						continue;
					}

					if(int size = g1->font->GetKernAmount(g1->c, g2->c)) {
						g2->path.MovePoints(CPoint(size, 0));
						g2->width += size;
					}
				}
			}
		}

		// wrap rows

		if(s->m_wrap == _T("normal") || s->m_wrap == _T("even")) {
			int maxwidth = abs((int)(vertical ? frame.Height() : frame.Width()));
			int minwidth = 0;

			for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos)) {
				Row* r = rows.GetAt(rpos);

				POSITION brpos = NULL;

				if(s->m_wrap == _T("even")) {
					int fullwidth = 0;

					for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
						const Glyph* g = r->GetAt(gpos);

						fullwidth += g->width + g->spacing;
					}

					fullwidth = abs(fullwidth);

					if(fullwidth > maxwidth) {
						maxwidth = fullwidth / ((fullwidth / maxwidth) + 1);
						minwidth = maxwidth;
					}
				}

				int width = 0;

				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
					const Glyph* g = r->GetAt(gpos);

					width += g->width + g->spacing;

					if(brpos && abs(width) > maxwidth && g->c != Text::SP) {
						row.Attach(DNew Row());
						POSITION next = brpos;
						r->GetNext(next);
						do {
							row->AddHead(r->GetPrev(brpos));
						} while(brpos);
						rows.InsertBefore(rpos, row);
						while(!r->IsEmpty() && r->GetHeadPosition() != next) {
							r->RemoveHeadNoReturn();
						}
						g = r->GetAt(gpos = next);
						width = g->width + g->spacing;
					}

					if(abs(width) >= minwidth) {
						if(g->style.linebreak == _T("char")
								|| g->style.linebreak == _T("word") && g->c == Text::SP) {
							brpos = gpos;
						}
					}
				}
			}
		}

		// trim rows

		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) {
			Row* r = rows.GetAt(pos);

			while(!r->IsEmpty() && r->GetHead()->c == Text::SP) {
				r->RemoveHead();
			}

			while(!r->IsEmpty() && r->GetTail()->c == Text::SP) {
				r->RemoveTail();
			}
		}

		// calc fill width for each glyph

		CAtlList<Glyph*> glypsh2fill;
		int fill_id = 0;
		int fill_width = 0;

		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) {
			Row* r = rows.GetAt(pos);

			POSITION gpos = r->GetHeadPosition();
			while(gpos) {
				Glyph* g = r->GetNext(gpos);

				if(!glypsh2fill.IsEmpty() && fill_id && (g->style.fill.id != fill_id || !pos && !gpos)) {
					int w = (int)(g->style.fill.width * fill_width + 0.5);

					while(!glypsh2fill.IsEmpty()) {
						Glyph* g = glypsh2fill.RemoveTail();
						fill_width -= g->width;
						g->fill = w - fill_width;
					}

					ASSERT(glypsh2fill.IsEmpty());
					ASSERT(fill_width == 0);

					glypsh2fill.RemoveAll();
					fill_width = 0;
				}

				fill_id = g->style.fill.id;

				if(g->style.fill.id) {
					glypsh2fill.AddTail(g);
					fill_width += g->width;
				}
			}
		}

		// calc row sizes and total subtitle size

		CSize size(0, 0);

		if(s->m_direction.secondary == _T("left") || s->m_direction.secondary == _T("up")) {
			ReverseList(rows);
		}

		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) {
			Row* r = rows.GetAt(pos);

			if(s->m_direction.primary == _T("left") || s->m_direction.primary == _T("up")) {
				ReverseList(*r);
			}

			int w = 0, h = 0;

			r->width = 0;

			for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
				const Glyph* g = r->GetAt(gpos);

				w += g->width;
				if(gpos) {
					w += g->spacing;
				}
				h = max(h, g->ascent + g->descent);

				r->width += g->width;
				if(gpos) {
					r->width += g->spacing;
				}
				r->ascent = max(r->ascent, g->ascent);
				r->descent = max(r->descent, g->descent);
				r->border = max(r->border, g->GetBackgroundSize());
			}

			for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
				Glyph* g = r->GetAt(gpos);
				g->row_ascent = r->ascent;
				g->row_descent = r->descent;
			}

			if(vertical) {
				size.cx += h;
				size.cy = max(size.cy, w);
			} else {
				size.cx = max(size.cx, w);
				size.cy += h;
			}
		}

		// align rows and calc glyph positions

		rs = DNew RenderedSubtitle(spdrc, clip);

		CPoint p = GetAlignPoint(style.placement, scale, frame, size);
		CPoint org = GetAlignPoint(style.placement, scale, frame);

		// collision detection

		if(!s->m_animated) {
			int tlb = !rows.IsEmpty() ? rows.GetHead()->border : 0;
			int brb = !rows.IsEmpty() ? rows.GetTail()->border : 0;

			CRect r(p, size);
			m_sra.GetRect(r, s, style.placement.align, tlb, brb);
			org += r.TopLeft() - p;
			p = r.TopLeft();
		}

		CRect subrect(p, size);

		// continue positioning

		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) {
			Row* r = rows.GetAt(pos);

			CSize rsize;
			rsize.cx = rsize.cy = r->width;

			if(vertical) {
				p.y = GetAlignPoint(style.placement, scale, frame, rsize).y;

				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
					CAutoPtr<Glyph> g = r->GetAt(gpos);
					g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5) + r->ascent - g->ascent;
					g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5);
					p.y += g->width + g->spacing;
					rs->m_glyphs.AddTail(g);
				}

				p.x += r->ascent + r->descent;
			} else {
				p.x = GetAlignPoint(style.placement, scale, frame, rsize).x;

				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) {
					CAutoPtr<Glyph> g = r->GetAt(gpos);
					g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5);
					g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5) + r->ascent - g->ascent;
					p.x += g->width + g->spacing;
					rs->m_glyphs.AddTail(g);
				}

				p.y += r->ascent + r->descent;
			}
		}

		// bkg, precalc style.placement.path, transform

		pos = rs->m_glyphs.GetHeadPosition();
		while(pos) {
			Glyph* g = rs->m_glyphs.GetNext(pos);
			g->CreateBkg();
			g->CreateSplineCoeffs(spdrc);
			g->Transform(org, subrect);
		}

		// merge glyphs (TODO: merge 'fill' too)

		Glyph* g0 = NULL;

		pos = rs->m_glyphs.GetHeadPosition();
		while(pos) {
			POSITION cur = pos;

			Glyph* g = rs->m_glyphs.GetNext(pos);

			CRect r = g->bbox + g->tl;

			int size = (int)(g->GetBackgroundSize() + 0.5);
			int depth = (int)(g->GetShadowDepth() + 0.5);

			r.InflateRect(size, size);
			r.InflateRect(depth, depth);

			r.left >>= 6;
			r.top >>= 6;
			r.right = (r.right + 32) >> 6;
			r.bottom = (r.bottom + 32) >> 6;

			if((r & clip).IsRectEmpty()) { // clip
				rs->m_glyphs.RemoveAt(cur);
			} else if(g0 && g0->style.IsSimilar(g->style)) { // append
				CPoint o = g->tl - g0->tl;

				g->path.MovePoints(o);

				g0->path.types.Append(g->path.types);
				g0->path.points.Append(g->path.points);

				g->path_bkg.MovePoints(o);

				g0->path_bkg.types.Append(g->path_bkg.types);
				g0->path_bkg.points.Append(g->path_bkg.points);

				g0->bbox |= g->bbox + o;

				rs->m_glyphs.RemoveAt(cur);
			} else { // leave alone
				g0 = g;
			}
		}

		// rasterize

		pos = rs->m_glyphs.GetHeadPosition();
		while(pos) {
			rs->m_glyphs.GetNext(pos)->Rasterize();
		}

		// cache

		m_rsc.Add(s->m_name, rs);

		m_fc.Flush();

		return rs;
	}
Пример #5
0
extern "C" int main(int argc, wchar_t* argv[])
{
    TRACED_HOOK_HANDLE      hHook = new HOOK_TRACE_INFO();
    NTSTATUS                NtStatus;
    ULONG                   ACLEntries[1] = {0};
    UNICODE_STRING*         NameBuffer = NULL;

    ORIG_CreateFontIndirectW = CreateFontIndirectW;

    FORCE(LhInstallHook(
            ORIG_CreateFontIndirectW,
            IMPL_CreateFontIndirectW,
            (PVOID)0,
            hHook));
    FORCE(LhSetInclusiveACL(ACLEntries, 1, hHook));

    CreateFontIndirectW(0);
    CreateFontW(10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"system");
    LOGFONTA lf = {};
    CreateFontIndirectA(&lf);
    CreateFontA(12, 0, 0, 0, 400, 0, 0, 0, 2, 0, 0, 0, 0, "MARLETT");

#if 0
    ORIG_GetTextExtentPoint32A = GetTextExtentPoint32A;
    FORCE(LhInstallHook(
            ORIG_GetTextExtentPoint32A,
            IMPL_GetTextExtentPoint32A,
            (PVOID)0,
            hHook));
    HDC hdc = GetDC(NULL);
    SIZE size;
    FORCE(LhSetInclusiveACL(ACLEntries, 1, hHook));
    GetTextExtentPoint32W(hdc, L"abc", 3, &size);
    GetTextExtentPointW(hdc, L"abc", 3, &size);
    GetTextExtentPoint32A(hdc, "abc", 3, &size);
    GetTextExtentPointA(hdc, "abc", 3, &size);
#endif

#if 0
    ORIG_MessageBeepHook = MessageBeep;
    /*
        The following shows how to install and remove local hooks...
    */
    FORCE(LhInstallHook(
            ORIG_MessageBeepHook,
            MessageBeepHook,
            (PVOID)0,
            hHook));

    printf(".\n");
    // won't invoke the hook handler because hooks are inactive after installation
    MessageBeep(123);
    getch();

    BOOL flags = 1;
    FORCE(LhIsThreadIntercepted(hHook, 0, &flags));
    printf("Intercepted %d\n", flags);
    // activate the hook for the current thread
    FORCE(LhSetInclusiveACL(ACLEntries, 1, hHook));
    FORCE(LhIsThreadIntercepted(hHook, 0, &flags));
    printf("Intercepted %d\n", flags);

    printf(".\n");
    // will be redirected into the handler...
    MessageBeep(123);
    getch();

    FORCE(LhSetGlobalExclusiveACL(ACLEntries, 1));
    printf(".\n");
    // will be redirected into the handler...
    MessageBeep(123);
    getch();

    FORCE(LhSetGlobalInclusiveACL(ACLEntries, 1));
    printf(".\n");
    // will be redirected into the handler...
    MessageBeep(123);
    getch();

    printf(".\n");
    // won't invoke the hook handler because hooks are inactive after installation
    ORIG_MessageBeepHook(123);
    getch();
#endif
    // this will also invalidate "hHook", because it is a traced handle...
    LhUninstallAllHooks();
    // this will do nothing because the hook is already removed...
    LhUninstallHook(hHook);

    printf(".\n");
    // will be redirected into the handler...
    MessageBeep(123);
    getch();

    // now we can safely release the traced handle
    delete hHook;

    hHook = NULL;

    // even if the hook is removed, we need to wait for memory release
    LhWaitForPendingRemovals();

    return 0;

ERROR_ABORT:

    if(hHook != NULL)
        delete hHook;

    if(NameBuffer != NULL)
        free(NameBuffer );

    printf("\n[Error(0x%p)]: \"%S\" (code: %d {0x%p})\n", (PVOID)NtStatus, RtlGetLastErrorString(), RtlGetLastError(), (PVOID)RtlGetLastError());

    _getch();

    return NtStatus;
}
int CBFontTT::FormatTextW(int X, int Y, BYTE* Text, HDC hDC, int Width, int MaxHeight)
{
	SIZE Size;
	GetTextExtentPoint32(hDC, "Ay", 2, &Size);
	int LineHeight = Size.cy;

	int TextLen = wcslen((wchar_t*)Text);
	wchar_t* Str = (wchar_t*)Text;
	int LastSpace = -1;
	int CurrY = 0;

	if(TextLen==0) return 0;

	while(true)
	{
		if(MaxHeight >=0 && CurrY + LineHeight > MaxHeight) return CurrY;

		int i=0;
		LastSpace = -1;

		do
		{
			i++;
			if(Str[i-1]=='\n')
			{
				i--;
				break;
			}
			GetTextExtentPoint32W(hDC, Str, i, &Size);
			if(Size.cx <= Width)
			{
				if(Str[i-1]==' ') LastSpace = i;				
			}
		}
		while(Str + i <(wchar_t*)Text+TextLen && Size.cx <= Width);

		// safety break
		if(i==1 && Size.cx > Width) return CurrY;

		// we encountered a newline
		if(Size.cx > Width)
		{
			if(LastSpace>=0) i = LastSpace-1;
			else i--;
		}

		UINT Flags = ETO_OPAQUE;
		if(Game->m_TextRTL) Flags |= ETO_RTLREADING;
		ExtTextOutW(hDC, X, Y + CurrY, Flags, NULL, Str, i, NULL);
		if(LastSpace>=0) i++;
		Str+=i;
		if(Str>(wchar_t*)Text+TextLen)
		{
			CurrY+=LineHeight;
			break;
		}

		if(Str[0]=='\n') Str++;
		CurrY+=LineHeight;

		// we're at the end
		if(Str>=(wchar_t*)Text+TextLen) break;
	}
	return CurrY /*+ LineHeight*/;
}
Пример #7
0
VOID GUIFont::GetTextSize( LPWSTR _pStr, SIZE& _Size )
{
	GetTextExtentPoint32W( m_hDC, _pStr, lstrlen( _pStr ), &_Size );
}
Пример #8
0
inline void GdiFont::CacheCharacter(unsigned int idx, wchar_t c)
{
	if (idx < font_count && NULL == (m_FontGlyphs[idx].t))
	{
		wchar_t sChar[2];
		sChar[0] = c;
		sChar[1] = L'\0';

		//TEXTMETRICW tm;
		//GetTextMetricsW(m_hMemDC,&tm);

		SIZE szChar = {0,0};
		GetTextExtentPoint32W(m_hMemDC,sChar,1,&szChar);

		int font_w = szChar.cx;
		int font_h = szChar.cy;

		if (m_ptLetter.x + font_w >= tex_size)
		{
			m_ptLetter.x = 0;
			if(m_ptLetter.y + font_h >= tex_size - font_h)
			{
				m_ptLetter.y = 0;

				HTEXTURE hTexLetter = m_pHGE->Texture_Create(tex_size,tex_size);
				if (NULL == (hTexLetter)) return;
				m_FontTextures.push_back(hTexLetter);
				m_hTexLetter = hTexLetter;

				RECT rcFill = {0,0,tex_size,tex_size};
				FillRect(m_hMemDC,&rcFill,m_hBrush);
			}
			else
				m_ptLetter.y += font_h;
		}
		
		TextOutW(m_hMemDC,m_ptLetter.x,m_ptLetter.y,(LPCWSTR)sChar,1);

		DWORD *target_pixels = m_pHGE->Texture_Lock(m_hTexLetter,FALSE);
		
		if (target_pixels)
		{
			BYTE* pbm = (BYTE*)m_pBuffer;

			int tex_w = m_pHGE->Texture_GetWidth(m_hTexLetter);
			int tex_h = m_pHGE->Texture_GetHeight(m_hTexLetter);

			// 不带背景色绘制字体
			for (int y=0;y<tex_w;y++)
			{
				for (int x=0;x<tex_h;x++)
				{
					BYTE alpha = 0;

					// 绘制字体
					target_pixels[y*tex_w+x] = ARGB(0xFFFFFF == RGB(pbm[2],pbm[1],pbm[0])?0xFF:0x0,0xFF,0xFF,0xFF);

					pbm+=4;
				}
			}
		}

		m_pHGE->Texture_Unlock(m_hTexLetter);

		m_FontGlyphs[idx].x = static_cast<float>(m_ptLetter.x);
		m_FontGlyphs[idx].y = static_cast<float>(m_ptLetter.y);
		m_FontGlyphs[idx].w = static_cast<float>(font_w);
		m_FontGlyphs[idx].h = static_cast<float>(font_h);
		m_FontGlyphs[idx].t = m_hTexLetter;
		m_FontGlyphs[idx].c = c;

		m_ptLetter.x += font_w;
	}
}
Пример #9
0
/**********************************************************************
 *          EMFDRV_ExtTextOut
 */
BOOL EMFDRV_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *lprect,
                        LPCWSTR str, UINT count, const INT *lpDx )
{
    EMREXTTEXTOUTW *pemr;
    DWORD nSize;
    BOOL ret;
    int textHeight = 0;
    int textWidth = 0;
    const UINT textAlign = GetTextAlign( dev->hdc );
    const INT graphicsMode = GetGraphicsMode( dev->hdc );
    FLOAT exScale, eyScale;

    nSize = sizeof(*pemr) + ((count+1) & ~1) * sizeof(WCHAR) + count * sizeof(INT);

    TRACE("%s %s count %d nSize = %d\n", debugstr_wn(str, count),
           wine_dbgstr_rect(lprect), count, nSize);
    pemr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nSize);

    if (graphicsMode == GM_COMPATIBLE)
    {
        const INT horzSize = GetDeviceCaps( dev->hdc, HORZSIZE );
        const INT horzRes  = GetDeviceCaps( dev->hdc, HORZRES );
        const INT vertSize = GetDeviceCaps( dev->hdc, VERTSIZE );
        const INT vertRes  = GetDeviceCaps( dev->hdc, VERTRES );
        SIZE wndext, vportext;

        GetViewportExtEx( dev->hdc, &vportext );
        GetWindowExtEx( dev->hdc, &wndext );
        exScale = 100.0 * ((FLOAT)horzSize  / (FLOAT)horzRes) /
                          ((FLOAT)wndext.cx / (FLOAT)vportext.cx);
        eyScale = 100.0 * ((FLOAT)vertSize  / (FLOAT)vertRes) /
                          ((FLOAT)wndext.cy / (FLOAT)vportext.cy);
    }
    else
    {
        exScale = 0.0;
        eyScale = 0.0;
    }

    pemr->emr.iType = EMR_EXTTEXTOUTW;
    pemr->emr.nSize = nSize;
    pemr->iGraphicsMode = graphicsMode;
    pemr->exScale = exScale;
    pemr->eyScale = eyScale;
    pemr->emrtext.ptlReference.x = x;
    pemr->emrtext.ptlReference.y = y;
    pemr->emrtext.nChars = count;
    pemr->emrtext.offString = sizeof(*pemr);
    memcpy((char*)pemr + pemr->emrtext.offString, str, count * sizeof(WCHAR));
    pemr->emrtext.fOptions = flags;
    if(!lprect) {
        pemr->emrtext.rcl.left = pemr->emrtext.rcl.top = 0;
        pemr->emrtext.rcl.right = pemr->emrtext.rcl.bottom = -1;
    } else {
        pemr->emrtext.rcl.left = lprect->left;
        pemr->emrtext.rcl.top = lprect->top;
        pemr->emrtext.rcl.right = lprect->right;
        pemr->emrtext.rcl.bottom = lprect->bottom;
    }

    pemr->emrtext.offDx = pemr->emrtext.offString + ((count+1) & ~1) * sizeof(WCHAR);
    if(lpDx) {
        UINT i;
        SIZE strSize;
        memcpy((char*)pemr + pemr->emrtext.offDx, lpDx, count * sizeof(INT));
        for (i = 0; i < count; i++) {
            textWidth += lpDx[i];
        }
        if (GetTextExtentPoint32W( dev->hdc, str, count, &strSize ))
            textHeight = strSize.cy;
    }
    else {
        UINT i;
        INT *dx = (INT *)((char*)pemr + pemr->emrtext.offDx);
        SIZE charSize;
        for (i = 0; i < count; i++) {
            if (GetTextExtentPoint32W( dev->hdc, str + i, 1, &charSize )) {
                dx[i] = charSize.cx;
                textWidth += charSize.cx;
                textHeight = max(textHeight, charSize.cy);
            }
        }
    }

    if (!lprect)
    {
        pemr->rclBounds.left = pemr->rclBounds.top = 0;
        pemr->rclBounds.right = pemr->rclBounds.bottom = -1;
        goto no_bounds;
    }

    switch (textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER)) {
    case TA_CENTER: {
        pemr->rclBounds.left  = x - (textWidth / 2) - 1;
        pemr->rclBounds.right = x + (textWidth / 2) + 1;
        break;
    }
    case TA_RIGHT: {
        pemr->rclBounds.left  = x - textWidth - 1;
        pemr->rclBounds.right = x;
        break;
    }
    default: { /* TA_LEFT */
        pemr->rclBounds.left  = x;
        pemr->rclBounds.right = x + textWidth + 1;
    }
    }

    switch (textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE)) {
    case TA_BASELINE: {
        TEXTMETRICW tm;
        if (!GetTextMetricsW( dev->hdc, &tm ))
            tm.tmDescent = 0;
        /* Play safe here... it's better to have a bounding box */
        /* that is too big than too small. */
        pemr->rclBounds.top    = y - textHeight - 1;
        pemr->rclBounds.bottom = y + tm.tmDescent + 1;
        break;
    }
    case TA_BOTTOM: {
        pemr->rclBounds.top    = y - textHeight - 1;
        pemr->rclBounds.bottom = y;
        break;
    }
    default: { /* TA_TOP */
        pemr->rclBounds.top    = y;
        pemr->rclBounds.bottom = y + textHeight + 1;
    }
    }
    EMFDRV_UpdateBBox( dev, &pemr->rclBounds );

no_bounds:
    ret = EMFDRV_WriteRecord( dev, &pemr->emr );
    HeapFree( GetProcessHeap(), 0, pemr );
    return ret;
}
Пример #10
0
Файл: ime.c Проект: AndreRH/wine
/*****
 * Internal functions to help with IME window management
 */
static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd)
{
    PAINTSTRUCT ps;
    RECT rect;
    HDC hdc;
    LPCOMPOSITIONSTRING compstr;
    LPBYTE compdata = NULL;
    HMONITOR monitor;
    MONITORINFO mon_info;
    INT offX=0, offY=0;
    LPINPUTCONTEXT lpIMC;

    lpIMC = LockRealIMC(hIMC);
    if (lpIMC == NULL)
        return;

    hdc = BeginPaint(hwnd,&ps);

    GetClientRect(hwnd,&rect);
    FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));

    compdata = ImmLockIMCC(lpIMC->hCompStr);
    compstr = (LPCOMPOSITIONSTRING)compdata;

    if (compstr->dwCompStrLen && compstr->dwCompStrOffset)
    {
        SIZE size;
        POINT pt;
        HFONT oldfont = NULL;
        LPWSTR CompString;
        LPIMEPRIVATE myPrivate;

        CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset);
        myPrivate = ImmLockIMCC(lpIMC->hPrivate);

        if (myPrivate->textfont)
            oldfont = SelectObject(hdc,myPrivate->textfont);

        ImmUnlockIMCC(lpIMC->hPrivate);

        GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size);
        pt.x = size.cx;
        pt.y = size.cy;
        LPtoDP(hdc,&pt,1);

        /*
         * How this works based on tests on windows:
         * CFS_POINT: then we start our window at the point and grow it as large
         *    as it needs to be for the string.
         * CFS_RECT:  we still use the ptCurrentPos as a starting point and our
         *    window is only as large as we need for the string, but we do not
         *    grow such that our window exceeds the given rect.  Wrapping if
         *    needed and possible.   If our ptCurrentPos is outside of our rect
         *    then no window is displayed.
         * CFS_FORCE_POSITION: appears to behave just like CFS_POINT
         *    maybe because the default MSIME does not do any IME adjusting.
         */
        if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT)
        {
            POINT cpt = lpIMC->cfCompForm.ptCurrentPos;
            ClientToScreen(lpIMC->hWnd,&cpt);
            rect.left = cpt.x;
            rect.top = cpt.y;
            rect.right = rect.left + pt.x;
            rect.bottom = rect.top + pt.y;
            monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY);
        }
        else /* CFS_DEFAULT */
        {
            /* Windows places the default IME window in the bottom left */
            HWND target = lpIMC->hWnd;
            if (!target) target = GetFocus();

            GetWindowRect(target,&rect);
            rect.top = rect.bottom;
            rect.right = rect.left + pt.x + 20;
            rect.bottom = rect.top + pt.y + 20;
            offX=offY=10;
            monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY);
        }

        if (lpIMC->cfCompForm.dwStyle == CFS_RECT)
        {
            RECT client;
            client =lpIMC->cfCompForm.rcArea;
            MapWindowPoints( lpIMC->hWnd, 0, (POINT *)&client, 2 );
            IntersectRect(&rect,&rect,&client);
            /* TODO:  Wrap the input if needed */
        }

        if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT)
        {
            /* make sure we are on the desktop */
            mon_info.cbSize = sizeof(mon_info);
            GetMonitorInfoW(monitor, &mon_info);

            if (rect.bottom > mon_info.rcWork.bottom)
            {
                int shift = rect.bottom - mon_info.rcWork.bottom;
                rect.top -= shift;
                rect.bottom -= shift;
            }
            if (rect.left < 0)
            {
                rect.right -= rect.left;
                rect.left = 0;
            }
            if (rect.right > mon_info.rcWork.right)
            {
                int shift = rect.right - mon_info.rcWork.right;
                rect.left -= shift;
                rect.right -= shift;
            }
        }

        SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE);

        TextOutW(hdc, offX,offY, CompString, compstr->dwCompStrLen);

        if (oldfont)
            SelectObject(hdc,oldfont);
    }

    ImmUnlockIMCC(lpIMC->hCompStr);

    EndPaint(hwnd,&ps);
    UnlockRealIMC(hIMC);
}
Пример #11
0
RectF TextRenderGdi::Measure(const WCHAR *s, size_t sLen) {
    SIZE txtSize;
    GetTextExtentPoint32W(hdcForTextMeasure, s, (int) sLen, &txtSize);
    RectF res(0.0f, 0.0f, (float) txtSize.cx, (float) txtSize.cy);
    return res;
}
Пример #12
0
void
gfxGDIFont::Initialize()
{
    NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak");

    LOGFONTW logFont;

    // Figure out if we want to do synthetic oblique styling.
    GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry());
    bool wantFakeItalic =
        (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) &&
        !fe->IsItalic();

    // If the font's family has an actual italic face (but font matching
    // didn't choose it), we have to use a cairo transform instead of asking
    // GDI to italicize, because that would use a different face and result
    // in a possible glyph ID mismatch between shaping and rendering.
    //
    // The font entry's mFamilyHasItalicFace flag is needed for user fonts
    // where the *CSS* family may not know about italic faces that are present
    // in the *GDI* family, and which GDI would use if we asked it to perform
    // the "italicization".
    bool useCairoFakeItalic = wantFakeItalic &&
        (fe->Family()->HasItalicFace() || fe->mFamilyHasItalicFace);

    if (mAdjustedSize == 0.0) {
        mAdjustedSize = mStyle.size;
        if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) {
            // to implement font-size-adjust, we first create the "unadjusted" font
            FillLogFont(logFont, mAdjustedSize,
                        wantFakeItalic && !useCairoFakeItalic);
            mFont = ::CreateFontIndirectW(&logFont);

            // initialize its metrics so we can calculate size adjustment
            Initialize();

            // calculate the properly adjusted size, and then proceed
            // to recreate mFont and recalculate metrics
            gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
            mAdjustedSize = mStyle.GetAdjustedSize(aspect);

            // delete the temporary font and metrics
            ::DeleteObject(mFont);
            mFont = nullptr;
            delete mMetrics;
            mMetrics = nullptr;
        }
    }

    // (bug 724231) for local user fonts, we don't use GDI's synthetic bold,
    // as it could lead to a different, incompatible face being used
    // but instead do our own multi-striking
    if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) {
        mApplySyntheticBold = true;
    }

    // this may end up being zero
    mAdjustedSize = ROUND(mAdjustedSize);
    FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
    mFont = ::CreateFontIndirectW(&logFont);

    mMetrics = new gfxFont::Metrics;
    ::memset(mMetrics, 0, sizeof(*mMetrics));

    AutoDC dc;
    SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
    AutoSelectFont selectFont(dc.GetDC(), mFont);

    // Get font metrics if size > 0
    if (mAdjustedSize > 0.0) {

        OUTLINETEXTMETRIC oMetrics;
        TEXTMETRIC& metrics = oMetrics.otmTextMetrics;

        if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
            mMetrics->superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y;
            // Some fonts have wrong sign on their subscript offset, bug 410917.
            mMetrics->subscriptOffset = fabs((double)oMetrics.otmptSubscriptOffset.y);
            mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
            mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
            mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
            mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;

            const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
            GLYPHMETRICS gm;
            DWORD len = GetGlyphOutlineW(dc.GetDC(), PRUnichar('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix);
            if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
                // 56% of ascent, best guess for true type
                mMetrics->xHeight =
                    ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
            } else {
                mMetrics->xHeight = gm.gmptGlyphOrigin.y;
            }
            mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
            gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
            mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
            mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
            if (oMetrics.otmEMSquare > 0) {
                mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare);
            }
        } else {
            // Make a best-effort guess at extended metrics
            // this is based on general typographic guidelines

            // GetTextMetrics can fail if the font file has been removed
            // or corrupted recently.
            BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
            if (!result) {
                NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
                mIsValid = false;
                memset(mMetrics, 0, sizeof(*mMetrics));
                return;
            }

            mMetrics->xHeight =
                ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR);
            mMetrics->superscriptOffset = mMetrics->xHeight;
            mMetrics->subscriptOffset = mMetrics->xHeight;
            mMetrics->strikeoutSize = 1;
            mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight
            mMetrics->underlineSize = 1;
            mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
            mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
            mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
            mMetrics->emDescent = metrics.tmDescent;
        }

        mMetrics->internalLeading = metrics.tmInternalLeading;
        mMetrics->externalLeading = metrics.tmExternalLeading;
        mMetrics->maxHeight = metrics.tmHeight;
        mMetrics->maxAscent = metrics.tmAscent;
        mMetrics->maxDescent = metrics.tmDescent;
        mMetrics->maxAdvance = metrics.tmMaxCharWidth;
        mMetrics->aveCharWidth = NS_MAX<gfxFloat>(1, metrics.tmAveCharWidth);
        // The font is monospace when TMPF_FIXED_PITCH is *not* set!
        // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
        if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
            mMetrics->maxAdvance = mMetrics->aveCharWidth;
        }

        // Cache the width of a single space.
        SIZE size;
        GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
        mMetrics->spaceWidth = ROUND(size.cx);

        // Cache the width of digit zero.
        // XXX MSDN (http://msdn.microsoft.com/en-us/library/ms534223.aspx)
        // does not say what the failure modes for GetTextExtentPoint32 are -
        // is it safe to assume it will fail iff the font has no '0'?
        if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size)) {
            mMetrics->zeroOrAveCharWidth = ROUND(size.cx);
        } else {
            mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
        }

        mSpaceGlyph = 0;
        if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
            WORD glyph;
            DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
                                         GGI_MARK_NONEXISTING_GLYPHS);
            if (ret != GDI_ERROR && glyph != 0xFFFF) {
                mSpaceGlyph = glyph;
            }
        }

        SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
    }

    if (IsSyntheticBold()) {
        mMetrics->aveCharWidth += GetSyntheticBoldOffset();
        mMetrics->maxAdvance += GetSyntheticBoldOffset();
    }

    mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
                                                                mFont);

    cairo_matrix_t sizeMatrix, ctm;
    cairo_matrix_init_identity(&ctm);
    cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);

    if (useCairoFakeItalic) {
        // Skew the matrix to do fake italic if it wasn't already applied
        // via the LOGFONT
        double skewfactor = OBLIQUE_SKEW_FACTOR;
        cairo_matrix_t style;
        cairo_matrix_init(&style,
                          1,                //xx
                          0,                //yx
                          -1 * skewfactor,  //xy
                          1,                //yy
                          0,                //x0
                          0);               //y0
        cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
    }

    cairo_font_options_t *fontOptions = cairo_font_options_create();
    if (mAntialiasOption != kAntialiasDefault) {
        cairo_font_options_set_antialias(fontOptions,
            GetCairoAntialiasOption(mAntialiasOption));
    }
    mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
                                           &ctm, fontOptions);
    cairo_font_options_destroy(fontOptions);

    if (!mScaledFont ||
        cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
#ifdef DEBUG
        char warnBuf[1024];
        sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
                mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
        NS_WARNING(warnBuf);
#endif
        mIsValid = false;
    } else {
        mIsValid = true;
    }

#if 0
    printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this,
           NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no"));
    printf("    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
    printf("    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
    printf("    internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
    printf("    spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->xHeight);
    printf("    uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n",
           mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize,
           mMetrics->superscriptOffset, mMetrics->subscriptOffset);
#endif
}
Пример #13
0
static VOID
MonthCalPaint(IN PMONTHCALWND infoPtr,
              IN HDC hDC,
              IN LPRECT prcUpdate)
{
    LONG x, y;
    RECT rcCell;
    COLORREF crOldText, crOldCtrlText = CLR_INVALID;
    HFONT hOldFont;
    INT iOldBkMode;

#if MONTHCAL_CTRLBG != MONTHCAL_DISABLED_CTRLBG
    if (!infoPtr->Enabled)
    {
        FillRect(hDC,
                 prcUpdate,
                 GetSysColorBrush(MONTHCAL_DISABLED_CTRLBG));
    }
#endif

    iOldBkMode = SetBkMode(hDC,
                           TRANSPARENT);
    hOldFont = (HFONT)SelectObject(hDC,
                                   infoPtr->hFont);

    for (y = prcUpdate->top / infoPtr->CellSize.cy;
         y <= prcUpdate->bottom / infoPtr->CellSize.cy && y < 7;
         y++)
    {
        rcCell.top = y * infoPtr->CellSize.cy;
        rcCell.bottom = rcCell.top + infoPtr->CellSize.cy;

        if (y == 0)
        {
            RECT rcHeader;

            /* paint the header */
            rcHeader.left = prcUpdate->left;
            rcHeader.top = rcCell.top;
            rcHeader.right = prcUpdate->right;
            rcHeader.bottom = rcCell.bottom;

            FillRect(hDC,
                     &rcHeader,
                     infoPtr->hbHeader);

            crOldText = SetTextColor(hDC,
                                     GetSysColor(infoPtr->Enabled ? MONTHCAL_HEADERFG : MONTHCAL_DISABLED_HEADERFG));

            for (x = prcUpdate->left / infoPtr->CellSize.cx;
                 x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
                 x++)
            {
                rcCell.left = x * infoPtr->CellSize.cx;
                rcCell.right = rcCell.left + infoPtr->CellSize.cx;

                /* write the first letter of each weekday */
                DrawTextW(hDC,
                          &infoPtr->Week[x],
                          1,
                          &rcCell,
                          DT_SINGLELINE | DT_NOPREFIX | DT_CENTER | DT_VCENTER);
            }

            SetTextColor(hDC,
                         crOldText);
        }
        else
        {
            if (crOldCtrlText == CLR_INVALID)
            {
                crOldCtrlText = SetTextColor(hDC,
                                             GetSysColor(infoPtr->Enabled ? MONTHCAL_CTRLFG : MONTHCAL_DISABLED_CTRLFG));
            }

            for (x = prcUpdate->left / infoPtr->CellSize.cx;
                 x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
                 x++)
            {
                UINT Day = infoPtr->Days[y - 1][x];

                rcCell.left = x * infoPtr->CellSize.cx;
                rcCell.right = rcCell.left + infoPtr->CellSize.cx;

                /* write the day number */
                if (Day != 0 && Day < 100)
                {
                    WCHAR szDay[3];
                    INT szDayLen;
                    RECT rcText;
                    SIZE TextSize;

                    szDayLen = swprintf(szDay,
                                         L"%lu",
                                         Day);

                    if (GetTextExtentPoint32W(hDC,
                                              szDay,
                                              szDayLen,
                                              &TextSize))
                    {
                        RECT rcHighlight = { 0, 0, 0, 0 };

                        rcText.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSize.cx / 2);
                        rcText.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSize.cy / 2);
                        rcText.right = rcText.left + TextSize.cx;
                        rcText.bottom = rcText.top + TextSize.cy;

                        if (Day == infoPtr->Day)
                        {
                            SIZE TextSel;

                            TextSel.cx = (infoPtr->CellSize.cx * 2) / 3;
                            TextSel.cy = (infoPtr->CellSize.cy * 3) / 4;

                            if (TextSel.cx < rcText.right - rcText.left)
                                TextSel.cx = rcText.right - rcText.left;
                            if (TextSel.cy < rcText.bottom - rcText.top)
                                TextSel.cy = rcText.bottom - rcText.top;

                            rcHighlight.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSel.cx / 2);
                            rcHighlight.right = rcHighlight.left + TextSel.cx;
                            rcHighlight.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSel.cy / 2);
                            rcHighlight.bottom = rcHighlight.top + TextSel.cy;

                            InflateRect(&rcHighlight,
                                        GetSystemMetrics(SM_CXFOCUSBORDER),
                                        GetSystemMetrics(SM_CYFOCUSBORDER));

                            if (!FillRect(hDC,
                                          &rcHighlight,
                                          infoPtr->hbSelection))
                            {
                                goto FailNoHighlight;
                            }

                            /* highlight the selected day */
                            crOldText = SetTextColor(hDC,
                                                     GetSysColor(infoPtr->Enabled ? MONTHCAL_SELFG : MONTHCAL_DISABLED_SELFG));
                        }
                        else
                        {
FailNoHighlight:
                            /* don't change the text color, we're not highlighting it... */
                            crOldText = CLR_INVALID;
                        }

                        TextOutW(hDC,
                                 rcText.left,
                                 rcText.top,
                                 szDay,
                                 szDayLen);

                        if (Day == infoPtr->Day && crOldText != CLR_INVALID)
                        {
                            if (infoPtr->HasFocus && infoPtr->Enabled && !(infoPtr->UIState & UISF_HIDEFOCUS))
                            {
                                COLORREF crOldBk;

                                crOldBk = SetBkColor(hDC,
                                                     GetSysColor(infoPtr->Enabled ? MONTHCAL_SELBG : MONTHCAL_DISABLED_SELBG));

                                DrawFocusRect(hDC,
                                              &rcHighlight);

                                SetBkColor(hDC,
                                           crOldBk);
                            }

                            SetTextColor(hDC,
                                         crOldText);
                        }
                    }
                }
            }
        }
    }

    if (crOldCtrlText != CLR_INVALID)
    {
        SetTextColor(hDC,
                     crOldCtrlText);
    }

    SetBkMode(hDC,
              iOldBkMode);
    SelectObject(hDC,
                 (HGDIOBJ)hOldFont);
}
Пример #14
0
/*
 * Returns bit combination
 *  FE_ABANDONFONT  - do not continue enumerating this font
 *  FE_FONTOK       - font was created and added to cache or already there
 */
int CALLBACK
FontEnum(
    LPENUMLOGFONTW lpLogFont,
    LPNEWTEXTMETRICW lpTextMetric,
    int nFontType,
    LPARAM lParam
    )

/*++

    Is called exactly once by GDI for each font in the system.  This
    routine is used to store the FONT_INFO structure.

--*/

{
    PFONTENUMDC pfed = (PFONTENUMDC)lParam;
    HDC hDC = pfed->hDC;
    BOOL bFindFaces = pfed->bFindFaces;
    HFONT hFont;
    TEXTMETRICW tmi;
    LONG nFont;
    LONG nFontNew;
    COORD SizeToShow;
    COORD SizeActual;
    COORD SizeWant;
    BYTE tmFamily;
    SIZE Size;
    LPWSTR pwszFace = lpLogFont->elfLogFont.lfFaceName;
    PFACENODE pFN;

    DBGFONTS(("  FontEnum \"%ls\" (%d,%d) weight 0x%lx(%d) -- %s\n",
            pwszFace,
            lpLogFont->elfLogFont.lfWidth, lpLogFont->elfLogFont.lfHeight,
            lpLogFont->elfLogFont.lfWeight, lpLogFont->elfLogFont.lfWeight,
            bFindFaces ? "Finding Faces" : "Creating Fonts"));

    //
    // reject variable width and italic fonts, also tt fonts with neg ac
    // also reject DBCS fonts because they are never really fixed pitch
    // they can be "binary" pitch meaning SBCS widths are the same as all
    // other SBCS widths and DBCS widhts the same as all DBCS widhts
    // (but DBCS widths won't be the same as SBCS widths)
    if
    (
      !(lpLogFont->elfLogFont.lfPitchAndFamily & FIXED_PITCH) ||
      (lpLogFont->elfLogFont.lfItalic)                        ||
      !(((NTMW_INTERNAL *)lpTextMetric)->tmd.fl & TMD_NONNEGATIVE_AC) ||
      (IS_ANY_DBCS_CHARSET(lpLogFont->elfLogFont.lfCharSet))
    )
    {
        DBGFONTS(("    REJECT  face (variable pitch, italic, or neg a&c)\n"));
        return bFindFaces ? TRUE : FALSE;  // unsuitable font
    }

    if (nFontType == TRUETYPE_FONTTYPE) {
        lpLogFont->elfLogFont.lfHeight = pfed->TTPointSize;
        lpLogFont->elfLogFont.lfWidth  = 0;
        lpLogFont->elfLogFont.lfWeight = FW_NORMAL;
    }

    /*
     * reject TT fonts for whoom family is not modern, that is do not use
     * FF_DONTCARE    // may be surprised unpleasantly
     * FF_DECORATIVE  // likely to be symbol fonts
     * FF_SCRIPT      // cursive, inappropriate for console
     * FF_SWISS OR FF_ROMAN // variable pitch
     */

    if ((nFontType == TRUETYPE_FONTTYPE) &&
            ((lpLogFont->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) {
        DBGFONTS(("    REJECT  face (TT but not FF_MODERN)\n"));
        return bFindFaces ? TRUE : FALSE;  // unsuitable font
    }

    /*
     * reject non-TT fonts that aren't OEM
     */
    if ((nFontType != TRUETYPE_FONTTYPE) &&
            (lpLogFont->elfLogFont.lfCharSet != OEM_CHARSET)) {
        DBGFONTS(("    REJECT  face (not TT nor OEM)\n"));
        return bFindFaces ? TRUE : FALSE;  // unsuitable font
    }

    /*
     * Add or find the facename
     */
    pFN = AddFaceNode(&gpFaceNames, pwszFace);
    if (pFN == NULL) {
        return FALSE;
    }

    if (bFindFaces) {
        if (nFontType == TRUETYPE_FONTTYPE) {
            DBGFONTS(("NEW TT FACE %ls\n", pwszFace));
            pFN->dwFlag |= EF_TTFONT;
        } else if (nFontType == RASTER_FONTTYPE) {
            DBGFONTS(("NEW OEM FACE %ls\n",pwszFace));
            pFN->dwFlag |= EF_OEMFONT;
        }
        return 0;
    }


    if (IS_BOLD(lpLogFont->elfLogFont.lfWeight)) {
        DBGFONTS2(("    A bold font (weight %d)\n", lpLogFont->elfLogFont.lfWeight));
        // return 0;
    }

    /* get font info */
    SizeWant.Y = (SHORT)lpLogFont->elfLogFont.lfHeight;
    SizeWant.X = (SHORT)lpLogFont->elfLogFont.lfWidth;
CreateBoldFont:
    lpLogFont->elfLogFont.lfQuality = NONANTIALIASED_QUALITY;
    hFont = CreateFontIndirectW(&lpLogFont->elfLogFont);
    ASSERT(hFont);
    if (!hFont) {
        DBGFONTS(("    REJECT  font (can't create)\n"));
        return 0;  // same font in other sizes may still be suitable
    }

    DBGFONTS2(("    hFont = %lx\n", hFont));

    //
    // for reasons unbeknownst to me, removing this code causes GDI
    // to yack, claiming that the font is owned by another process.
    //

    SelectObject(hDC,hFont);
    if (!GetTextMetricsW(hDC, &tmi)) {
        tmi = *((LPTEXTMETRICW)lpTextMetric);
    }

    if (GetTextExtentPoint32W(hDC, L"0", 1, &Size)) {
        SizeActual.X = (SHORT)Size.cx;
    } else {
        SizeActual.X = (SHORT)(tmi.tmMaxCharWidth);
    }
    SizeActual.Y = (SHORT)(tmi.tmHeight + tmi.tmExternalLeading);
    DBGFONTS2(("    actual size %d,%d\n", SizeActual.X, SizeActual.Y));
    tmFamily = tmi.tmPitchAndFamily;
    if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) {
        SizeToShow = SizeWant;
        if (SizeWant.X == 0) {
            // Asking for zero width height gets a default aspect-ratio width
            // It's better to show that width rather than 0.
            SizeToShow.X = SizeActual.X;
        }
    } else {
        SizeToShow = SizeActual;
    }
    DBGFONTS2(("    SizeToShow = (%d,%d), SizeActual = (%d,%d)\n",
            SizeToShow.X, SizeToShow.Y, SizeActual.X, SizeActual.Y));

    // there's a GDI bug - this assert fails occasionally
    //ASSERT (tmi.tmw.tmMaxCharWidth == lpTextMetric->tmMaxCharWidth);

    /*
     * NOW, determine whether this font entry has already been cached
     * LATER : it may be possible to do this before creating the font, if
     * we can trust the dimensions & other info from lpTextMetric.
     * Sort by size:
     *  1) By pixelheight (negative Y values)
     *  2) By height (as shown)
     *  3) By width (as shown)
     */
    for (nFont = 0; nFont < (LONG)NumberOfFonts; ++nFont) {
        COORD SizeShown;

        if (FontInfo[nFont].hFont == NULL) {
            DBGFONTS(("!   Font %x has a NULL hFont\n", nFont));
            continue;
        }


        if (FontInfo[nFont].SizeWant.X > 0) {
            SizeShown.X = FontInfo[nFont].SizeWant.X;
        } else {
            SizeShown.X = FontInfo[nFont].Size.X;
        }

        if (FontInfo[nFont].SizeWant.Y > 0) {
            // This is a font specified by cell height.
            SizeShown.Y = FontInfo[nFont].SizeWant.Y;
        } else {
            SizeShown.Y = FontInfo[nFont].Size.Y;
            if (FontInfo[nFont].SizeWant.Y < 0) {
                // This is a TT font specified by character height.
                if (SizeWant.Y < 0 && SizeWant.Y > FontInfo[nFont].SizeWant.Y) {
                    // Requested pixelheight is smaller than this one.
                    DBGFONTS(("INSERT %d pt at %x, before %d pt\n",
                            -SizeWant.Y, nFont, -FontInfo[nFont].SizeWant.Y));
                    nFontNew = nFont;
                    goto InsertNewFont;
                }
            }
        }

        // DBGFONTS(("    SizeShown(%x) = (%d,%d)\n",nFont,SizeShown.X,SizeShown.Y));

        if (SIZE_EQUAL(SizeShown, SizeToShow) &&
                FontInfo[nFont].Family == tmFamily &&
                FontInfo[nFont].Weight == tmi.tmWeight &&
                wcscmp(FontInfo[nFont].FaceName, pwszFace) == 0) {
            /*
             * Already have this font
             */
            DBGFONTS2(("    Already have the font\n"));
            DeleteObject(hFont);
            pfed->ulFE |= FE_FONTOK;
            return TRUE;
        }


        if ((SizeToShow.Y < SizeShown.Y) ||
                (SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) {
            /*
             * This new font is smaller than nFont
             */
            DBGFONTS(("INSERT at %x, SizeToShow = (%d,%d)\n", nFont,
                    SizeToShow.X,SizeToShow.Y));
            nFontNew = nFont;
            goto InsertNewFont;
        }
    }

    /*
     * The font we are adding should be appended to the list,
     * since it is bigger (or equal) to the last one.
     */
    nFontNew = (LONG)NumberOfFonts;

InsertNewFont: // at nFontNew

//  ASSERT ((lpTextMetric->tmPitchAndFamily & 1) == 0);
    /* If we have to grow our font table, do it */

    if (NumberOfFonts == FontInfoLength) {
        PFONT_INFO Temp;

        FontInfoLength += FONT_INCREMENT;
        Temp = (PFONT_INFO)HeapReAlloc(pConHeap,MAKE_TAG( FONT_TAG ),FontInfo,sizeof(FONT_INFO) * FontInfoLength);
        ASSERT(Temp);
        if (Temp == NULL) {
            FontInfoLength -= FONT_INCREMENT;
            return FALSE;
        }
        FontInfo = Temp;
    }

    if (nFontNew < (LONG)NumberOfFonts) {
        RtlMoveMemory(&FontInfo[nFontNew+1],
                &FontInfo[nFontNew],
                sizeof(FONT_INFO)*(NumberOfFonts - nFontNew));
    }

    /*
     * Store the font info
     */
    FontInfo[nFontNew].hFont = hFont;
    FontInfo[nFontNew].Family = tmFamily;
    FontInfo[nFontNew].Size = SizeActual;
    if (TM_IS_TT_FONT(tmFamily)) {
        FontInfo[nFontNew].SizeWant = SizeWant;
    } else {
        FontInfo[nFontNew].SizeWant.X = 0;
        FontInfo[nFontNew].SizeWant.Y = 0;
    }
    FontInfo[nFontNew].Weight = tmi.tmWeight;
    FontInfo[nFont].FaceName = pFN->awch;

    ++NumberOfFonts;

    if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(FontInfo[nFontNew].Weight)) {
          lpLogFont->elfLogFont.lfWeight = FW_BOLD;
          goto CreateBoldFont;
    }

    pfed->ulFE |= FE_FONTOK;  // and continue enumeration
    return TRUE;
}
Пример #15
0
FskErr winTextBox(FskTextEngineState state, FskBitmap bits, const char *text, UInt32 textLen, FskConstRectangle bounds, FskConstRectangleFloat boundsFloat, FskConstRectangle clipRect, FskConstColorRGBA color, UInt32 blendLevel, UInt32 textSize, UInt32 textStyle, UInt16 hAlign, UInt16 vAlign, FskFixed textExtra, const char *fontName, FskTextFormatCache formatCacheIn)
{
	FskTextFormatCacheGDI formatCache = (FskTextFormatCacheGDI)formatCacheIn;
	RECT r;
	UINT flags = DT_SINGLELINE | DT_NOPREFIX | ((kFskTextTruncateEnd & textStyle) ? DT_END_ELLIPSIS : 0);
	Boolean direct = (blendLevel >= 255) && (NULL != bits->hbmp) &&
			!((kFskTextOutline | kFskTextOutlineHeavy) & textStyle) && !bits->hasAlpha;
	FskBitmap scratchBits = NULL;
	HFONT font;
	HDC dc = direct ? bits->hdc : state->dc;
	HGDIOBJ saveFont;
	FskRectangleRecord clip;
	unsigned char scratchBuffer[256];
	int saveCharExtra;

	// combine bounds and clip to total clip
	if (NULL == clipRect) {
		clip = *bounds;
	}
	else {
		if (false == FskRectangleIntersect(clipRect, bounds, &clip))
			return kFskErrNone;
	}

	if (direct) {
		if (clipRect)
			IntersectClipRect(dc, clipRect->x, clipRect->y, clipRect->x + clipRect->width, clipRect->y + clipRect->height);
		SetRect(&r, bounds->x, bounds->y, bounds->x + bounds->width, bounds->y + bounds->height);

		SetTextColor(dc, RGB(color->r, color->g, color->b));
	}
	else {
		FskErr err;
		const UInt32 kFskTextOffscreenFormat = kFskBitmapFormat24BGR;

		/* Negative width below indicates that we want a native bitmap */
		err = FskBitmapNew(-bounds->width, bounds->height, kFskTextOffscreenFormat, &scratchBits);
		if (kFskErrNone == err)
			SetRect(&r, 0, 0, bounds->width, bounds->height);
		else {
			FskRectangleRecord b;

			err = winTextGetBounds(state, bits, text, textLen, textSize, textStyle, textExtra, fontName, &b, NULL, formatCacheIn);
			if (err) return err;

			err = FskBitmapNew(-b.width, b.height, kFskTextOffscreenFormat, &scratchBits);
			if (err) return err;

			SetRect(&r, 0, 0, b.width, b.height);
		}

		dc = scratchBits->hdc;
		SetTextColor(dc, RGB(255, 255, 255));
	}

	if (NULL == formatCache)
		saveFont = syncFont(textSize, textStyle, fontName, &font, state);
	else
		saveFont = SelectObject(dc, formatCache->font);

	saveCharExtra = SetTextCharacterExtra(dc, textExtra >> 16);

#if 0
	switch (hAlign) {
		case kFskTextAlignLeft:
		default:
			flags |= DT_LEFT;
			break;

		case kFskTextAlignCenter:
			flags |= DT_CENTER;
			break;

		case kFskTextAlignRight:
			flags |= DT_RIGHT;
			break;
	}

	switch (vAlign) {
		case kFskTextAlignTop:
		default:
			flags |= DT_TOP;
			break;

		case kFskTextAlignCenter:
			flags |= DT_VCENTER;
			break;

		case kFskTextAlignBottom:
			flags |= DT_BOTTOM;
			break;
	}
#endif

	{
	SIZE sz;
	UINT align;
	SInt32 y;
	char *encodedText = (char *)scratchBuffer;
	UInt32 encodedTextLen = sizeof(scratchBuffer);
	if (kFskErrNone != FskTextUTF8ToUnicode16NENoAlloc(text, textLen, (UInt16 *)scratchBuffer, &encodedTextLen)) {
		if (kFskErrNone != FskMemPtrNew(encodedTextLen, &encodedText)) {
			FskBitmapDispose(scratchBits);
			goto done;
		}
		FskTextUTF8ToUnicode16NENoAlloc(text, textLen, (UInt16 *)encodedText, &encodedTextLen);
	}

	encodedTextLen >>= 1;
	remapForMicrosoft((UInt16 *)encodedText, encodedTextLen);

	if (kFskTextTruncateCenter & textStyle) {
		// work hard to truncate the center, since Windows doesn't support this directly
		SIZE size;
		int *widths;

		if (kFskErrNone == FskMemPtrNew(sizeof(int) * encodedTextLen, (FskMemPtr *)&widths)) {
			int maxC, i, fitWidth = (r.right - r.left);
			GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, 32768, &maxC, widths, &size);
			if (size.cx > fitWidth) {
				SInt32 currentWidth = size.cx;
				SInt32 truncBegin, truncEnd;
				WCHAR ellipsis = 0x2026;
				SIZE ellipsisSize;
				UInt16 *uniChars = (UInt16 *)encodedText;

				for (i = encodedTextLen - 1; i > 0; i--)
					widths[i] -= widths[i - 1];

				GetTextExtentPoint32W(dc, (LPWSTR)&ellipsis, 1, &ellipsisSize);		//@@ could use ellipsisWidth in cache here
				fitWidth -= ellipsisSize.cx;

				if (fitWidth > 0) {
					Boolean phase = true;		// start towards the end
					truncBegin = truncEnd = encodedTextLen / 2;
					while ((currentWidth > fitWidth) && ((truncEnd - truncBegin) != encodedTextLen)) {
						if (phase) {
							if (truncEnd < (SInt32)encodedTextLen) {
								currentWidth -= widths[truncEnd];
								truncEnd += 1;
							}
						}
						else {
							if (0 != truncBegin) {
								truncBegin -= 1;
								currentWidth -= widths[truncBegin];
							}
						}
						phase = !phase;
					}
					FskMemMove(&uniChars[truncBegin + 1], &uniChars[truncEnd], (encodedTextLen - truncEnd) * 2);
					uniChars[truncBegin] = ellipsis;
					encodedTextLen -= (truncEnd - truncBegin);
					encodedTextLen += 1;

					flags &= ~DT_END_ELLIPSIS;
				}
			}

			FskMemPtrDispose(widths);
		}
	}

#if 0
	DrawTextW(dc, (LPWSTR)encodedText, encodedTextLen, &r, flags);
#else
	if (kFskTextTruncateEnd & textStyle) {
		int fitChars;
		int stackWidths[256], *widths, width = r.right - r.left;
		WCHAR ellipsis = 0x2026;
		SIZE ellipsisSz;

		if (encodedTextLen < 256)
			widths = stackWidths;
		else {
			if (kFskErrNone != FskMemPtrNew(sizeof(int) * encodedTextLen, (FskMemPtr *)&widths)) {
				widths = stackWidths;
				encodedTextLen = 256;
			}
		}

		GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, width, &fitChars, widths, &sz);
		if ((UInt32)fitChars < encodedTextLen) {
			// remove trailing white space
			if (formatCache) {
				if (!formatCache->haveEllipsisWidth) {
					GetTextExtentExPointW(dc, (WCHAR *)&ellipsis, 1, 0, NULL, NULL, &ellipsisSz);
					formatCache->haveEllipsisWidth = true;
					formatCache->ellipsisWidth = ellipsisSz.cx;
				}
				else
					ellipsisSz.cx = formatCache->ellipsisWidth;
			}
			else
				GetTextExtentExPointW(dc, (WCHAR *)&ellipsis, 1, 0, NULL, NULL, &ellipsisSz);

			if (width > ellipsisSz.cx) {
				width -= ellipsisSz.cx;

				while (fitChars > 2) {
					UInt16 c = ((UInt16 *)encodedText)[fitChars - 2];

					if ((32 != c) && (9 != c) && (0x3000 != c))
						break;

					fitChars -= 1;
				}

				// truncate if needed to make room for the ellipsis
				while ((widths[fitChars - 1] > width) && (fitChars > 2))
					fitChars -= 1;

				// add ellipsis
				((UInt16 *)encodedText)[fitChars - 1] = 0x2026;		// ellipsis
				encodedTextLen = fitChars;
			}
			else
				encodedTextLen = 0;
		}

		if (widths != stackWidths)
			FskMemPtrDispose(widths);
	}
	else {
		if (kFskTextAlignCenter == vAlign)
			GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, 0, NULL, NULL, &sz);
	}

	if (kFskTextAlignCenter == vAlign) {
		y = (r.top + r.bottom - sz.cy) >> 1;
		align = TA_TOP;
	}
	else
	if (kFskTextAlignTop == vAlign) {