truetype.cpp

来自「Windows 图形编程 书籍」· C++ 代码 · 共 1,028 行 · 第 1/2 页

CPP
1,028
字号
		// scale points
		for (int i=0; i<m_len; i++)
			sc.Map(m_Point[i]);

		// em square
		if ( mark )
		{
			TCHAR temp[64];
			wsprintf(temp, "Glyph: %d, Size %d Inst %d EM square %d", 
				m_glyphindex, m_glyphsize, m_instrsize, emsquare);
			TextOut(hDC, x, (int)(y-emsquare*scale)-40, temp, _tcslen(temp));

			if ( (mark==IDM_VIEW_LARGE) || (mark==IDM_VIEW_METRICS) )
			{
				// bounding box
				RECT r = { (int) (x + m_bound.left  * scale),
					   (int) (y - m_bound.bottom* scale),
					   (int) (x + m_bound.right * scale),
					   (int) (y - m_bound.top   * scale) };
				
				FrameRect(hDC, & r, GetSysColorBrush(COLOR_ACTIVECAPTION));
			}

			if ( mark!=IDM_VIEW_METRICS )
			{
				// em square
				HPEN hPen = CreatePen(PS_DOT, 1, 0);
				HGDIOBJ hOld = SelectObject(hDC, hPen);

				for (int i=0; i<=emsquare; i+=(emsquare/16))
				{
					sc.Line(hDC, i, 0, i, emsquare);
					sc.Line(hDC, 0, i, emsquare, i);
				}

				SelectObject(hDC, hOld);
				DeleteObject(hOld);
			}

			if ( mark==IDM_VIEW_METRICS )
			{
				// descender
				sc.Line(hDC, - emsquare/10, m_Descender, emsquare*11/10, m_Descender);

				// base
				sc.Line(hDC, - emsquare/10, 0, emsquare*11/10, 0);

				// ascender
				sc.Line(hDC, - emsquare/10, m_Ascender , emsquare*11/10, m_Ascender );

				sc.Line(hDC, m_bound.left - m_lsb,				    m_Descender,
							 m_bound.left - m_lsb,					m_Ascender);
				sc.Line(hDC, m_bound.left - m_lsb + m_advancewidth, m_Descender,  
							 m_bound.left - m_lsb + m_advancewidth, m_Ascender);


			}
		}

		i = 0;
		int x0, y0;

		// curve
		while ( i<m_len )
		{
			if ( m_Flag[i] & FLAG_OPEN )
			{
				x0 = m_Point[i].x; y0 = m_Point[i].y; MoveToEx(hDC, x0, y0, NULL);
			}
			else if ( m_Flag[i] & FLAG_ON )
			{
				x0 = m_Point[i].x; y0 = m_Point[i].y; LineTo(hDC, x0, y0);
			}
			else // off on, convert to cubic Bezier curve
			{
				// p0 p1 p2 -> p0 (p0+2p1)/3 (2p1+p2)/3, p2
				Bezier(hDC, x0, y0, m_Point[i].x, m_Point[i].y, m_Point[i+1].x, m_Point[i+1].y);
				i ++;
			}

			i ++;
		}

		// mark control points
		if ( mark && (mark!=IDM_VIEW_METRICS) )
		{
			int seq = 0;

			for (i=0; i<m_len; i++)
			{
				if ( (m_Flag[i] & FLAG_EXTRA)==0 )
				{
					if ( m_Flag[i] & FLAG_ON )
						Mark(hDC, m_Point[i].x, m_Point[i].y, RGB(0xFF, 0, 0));
					else 
						Mark(hDC, m_Point[i].x, m_Point[i].y, RGB(0, 0, 0xFF));

					if ( mark==3 )
					{
						TCHAR temp[4];
						wsprintf(temp, "%d", seq);

						TextOut(hDC, m_Point[i].x+5, m_Point[i].y-5, temp, _tcslen(temp));
					}
					seq ++;
				}
			}
		}
		return TRUE;
	}

};


int KTrueType::DecodeGlyph(int index, KCurve & curve, XFORM * xm) const
{
	const GlyphHeader * pHeader = GetGlyph(index);

	if ( pHeader==NULL )
	{
	//	assert(false);
		return 0;
	}

	int	nContour = (short) reverse(pHeader->numberOfContours);

	if ( nContour<0 )
	{
		return DecodeCompositeGlyph(pHeader+1, curve); // after the header
	}

	if ( nContour==0 )
		return 0;

	curve.SetBound(reverse((WORD)pHeader->xMin), reverse((WORD)pHeader->yMin), 
		           reverse((WORD)pHeader->xMax), reverse((WORD)pHeader->yMax));

	const USHORT * pEndPoint = (const USHORT *) (pHeader+1);

	int nPoints = reverse(pEndPoint[nContour-1]) + 1;  // endpoint of last contour + 1
	int nInst   = reverse(pEndPoint[nContour]);		   // instructon length	

	curve.m_glyphindex = index;
	curve.m_glyphsize  = (int) GetGlyph(index+1) - (int) GetGlyph(index);
	curve.m_Ascender   = m_Ascender;
	curve.m_Descender  = m_Descender;
	curve.m_LineGap    = m_LineGap;
	
	GetMetrics(index, curve.m_advancewidth, curve.m_lsb);
	
	if ( curve.m_glyphsize==0 )
		return 0;

	curve.m_instrsize  = nInst;
	
	const BYTE * pFlag = (const BYTE *) & pEndPoint[nContour] + 2 + nInst;	// first byte in flag
	const BYTE * pX    = pFlag;

	int xlen = 0;

	for (int i=0; i<nPoints; i++, pX++)
	{
		int unit = 0;

		switch ( pX[0] & G_XMASK )
		{
			case G_XADDBYTE:
			case G_XSUBBYTE:
				unit = 1;
				break;

			case G_XADDINT:
				unit = 2;
		}

		if ( pX[0] & G_REPEAT )
		{
			xlen += unit * (pX[1]+1); 

			i += pX[1];
			pX ++;
		}
		else
			xlen += unit;
	}

	const BYTE * pY = pX + xlen;

	int x = 0;
	int y = 0;

	i = 0;
	BYTE flag = 0;
	int  rep  = 0;

	for (int j=0; j<nContour; j++)
	{
		int limit = reverse(pEndPoint[j]);

		while ( i<=limit )
		{
			if ( rep==0 )
			{
				flag =  * pFlag++;
				rep  = 1;

				if ( flag & G_REPEAT )
					rep += * pFlag ++;
			}
		
			int dx = 0, dy = 0;

			switch ( flag & G_XMASK )
			{
				case G_XADDBYTE: dx =   pX[0]; pX ++; break;
				case G_XSUBBYTE: dx = - pX[0]; pX ++; break;
				case G_XADDINT:  dx = (signed short )( (pX[0] << 8) + pX[1]); pX+=2;
			}

			switch ( flag & G_YMASK )
			{
				case G_YADDBYTE: dy =   pY[0]; pY ++; break;
				case G_YSUBBYTE: dy = - pY[0]; pY ++; break;
				case G_YADDINT:  dy = (signed short )( (pY[0] << 8) + pY[1]); pY+=2;
			}

			x += dx;
			y += dy;
			assert(abs(x)<16384);
			assert(abs(y)<16384);

			if ( xm )
				curve.Add((int) ( x * xm->eM11 + y * xm->eM21 + xm->eDx ), 
					      (int) ( x * xm->eM12 + y * xm->eM22 + xm->eDy ), 
						  (flag & G_ONCURVE) ? KCurve::FLAG_ON : 0);
			else
				curve.Add(x, y, (flag & G_ONCURVE) ? KCurve::FLAG_ON : 0);
			
			rep --;
			i ++;
		}

		curve.Close();
	}

	return curve.GetLength();
}


int KTrueType::GetOutline(int index, KCurve & curve) const
{
	curve.Reset();
	
	return DecodeGlyph(index, curve, NULL);
}


void KTrueType::DrawTTGlyph(HDC hDC, int x, int y, int index, double scale, int mark)
{
	KCurve curve;
	
	GetOutline(index, curve);

	curve.Draw(hDC, x, y, scale, mark, m_unitsPerEm);
}



BOOL LogTableDirectory(KTrueType & tt, HINSTANCE hInstance, const char * FaceName)
{
	KLogWindow * pLog = new KLogWindow;

	if ( pLog==NULL )
		return FALSE;

	pLog->Create(hInstance, "Table Directory", LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FONT)));

	pLog->Log("%s \r\n", FaceName);
		
    int no = tt.GetTableNo();

	pLog->Log("%d tables\r\n\r\n", no);

	pLog->Log("Tag           CheckSum   Offset   Length\r\n");
	pLog->Log("----------------------------------------\r\n");

    for (int i=0; i<no; i++)
    {
		const TableEntry * pEntry = tt.GetTableEntry(i);

        unsigned long offset = reverse(pEntry->offset);
        unsigned long length = reverse(pEntry->length);
			
		pLog->Log("%c%c%c%c %08X %08X %8x %8x\r\n", 
				pEntry->tag[0], pEntry->tag[1], 
				pEntry->tag[2], pEntry->tag[3], 
				* ((DWORD *) pEntry->tag),
				pEntry->checkSum,
				offset, length);
    }

	return TRUE;
}



/*
HBITMAP GetBackgroundBitmap(HWND hWnd)
{
	ASSERT(::IsWindow( hWnd));

	HWND hParentWnd = NULL;
	HBITMAP hBitmap    = NULL;

	RECT rcWnd;
	RECT rcParentWnd;

	hParentWnd = ::GetParent( hWnd);

	if (!hParentWnd)
		return NULL;

	::GetWindowRect( hWnd, &rcWnd);

	rcParentWnd = rcWnd;
	::ScreenToClient( hParentWnd, (LPPOINT)&rcParentWnd);
	::ScreenToClient( hParentWnd, ((LPPOINT)&rcParentWnd)+1);
	::GetClientRect( hWnd, &rcWnd);

	HDC hParentDC = GetDC( hParentWnd);

	if(!hParentDC)
		return NULL;

	HDC hBkgDC = ::CreateCompatibleDC( hParentDC);

	if (!hBkgDC)
	{
		::ReleaseDC( hParentWnd, hParentDC);
		::DeleteDC( hParentDC);

		return NULL;
	}

	hBitmap = ::CreateCompatibleBitmap( hParentDC, rcWnd.right-rcWnd.left, 
												   rcWnd.bottom-rcWnd.top);

	HBITMAP hOldBitmap = (HBITMAP)::SelectObject( hBkgDC, hBitmap);

	::SendMessage( hParentWnd, WM_ERASEBKGND, (WPARAM)hParentDC, 0L);

	::BitBlt( hBkgDC, rcWnd.left, rcWnd.top, rcWnd.right-rcWnd.left, 
				rcWnd.bottom-rcWnd.top, hParentDC, rcParentWnd.left, rcParentWnd.top, 
				SRCCOPY);

	::SelectObject( hBkgDC, hOldBitmap);
	::DeleteObject( hOldBitmap);

	::DeleteDC( hBkgDC);

	::ReleaseDC( hParentWnd, hParentDC);
	::DeleteDC( hParentDC);

	return hBitmap;
}
*/

// Composite Glyph

typedef enum
{
	ARG_1_AND_2_ARE_WORDS		= 0x0001, // If this is set, the arguments are words; otherwise, they are bytes. 
	ARGS_ARE_XY_VALUES			= 0x0002, // If this is set, the arguments are xy values; otherwise, they are points. 
	ROUND_XY_TO_GRID			= 0x0004, // For the xy values if the preceding is true. 
	WE_HAVE_A_SCALE				= 0x0008, // This indicates that there is a simple scale for the component. Otherwise, scale = 1.0. 
	
	RESERVED					= 0x0010, // This bit is reserved. Set it to 0. 
	MORE_COMPONENTS				= 0x0020, // Indicates at least one more glyph after this one. 
	WE_HAVE_AN_X_AND_Y_SCALE	= 0x0040, // The x direction will use a different scale from the y direction. 
	WE_HAVE_A_TWO_BY_TWO		= 0x0080, // There is a 2 by 2 transformation that will be used to scale the component. 
	
	WE_HAVE_INSTRUCTIONS		= 0x0100, // Following the last component are instructions for the composite character. 
	USE_MY_METRICS				= 0x0200, // If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph. This works for hinted and unhinted characters. 
	OVERLAP_COMPOUND		    = 0x0400, // Used by Apple in GX fonts. 
	SCALED_COMPONENT_OFFSET     = 0x0800, // Composite designed to have the component offset scaled (designed for Apple rasterizer). 
	
	UNSCALED_COMPONENT_OFFSET   = 0x1000, // Composite designed not to have the component offset scaled (designed for the Microsoft TrueType rasterizer).
};


class KDataStream
{
	const unsigned char * pData;

public:
	unsigned char GetByte(void)
	{
		return * pData ++;
	}

	unsigned short GetWord(void)
	{
		unsigned short rslt = GetByte();
		
		return rslt * 256 + GetByte();
	}

	KDataStream(const void * pInput)
	{
		pData = (const unsigned char *) pInput;
	}
};

	       
int KTrueType::DecodeCompositeGlyph(const void * pGlyph, KCurve & curve) const
{
	KDataStream str(pGlyph);

	unsigned flags;

	int len = 0;

	do 
	{
		flags      = str.GetWord();
		
		unsigned glyphIndex = str.GetWord();

		// Argument1 and argument2 can be either x and y offsets to be added to the glyph or two point numbers. 
		// In the latter case, the first point number indicates the point that is to be matched to the new glyph. 
		// The second number indicates the new glyph's "matched" point. Once a glyph is added, its point numbers 
		// begin directly after the last glyphs (endpoint of first glyph + 1). 

		// When arguments 1 and 2 are an x and a y offset instead of points and the bit ROUND_XY_TO_GRID is set to 1, 
		// the values are rounded to those of the closest grid lines before they are added to the glyph. 
		// X and Y offsets are described in FUnits. 

		signed short argument1;
		signed short argument2;

		if ( flags & ARG_1_AND_2_ARE_WORDS )
		{
			argument1 = str.GetWord(); // (SHORT or FWord) argument1;
			argument2 = str.GetWord(); // (SHORT or FWord) argument2;
		} 
		else 
		{
			argument1 = (signed char) str.GetByte();
			argument2 = (signed char) str.GetByte();
		}
	
		signed short xscale, yscale, scale01, scale10;

		xscale  = 1;
		yscale  = 1;
		scale01 = 0;
		scale10 = 0;

		if ( flags & WE_HAVE_A_SCALE )
		{
			xscale = str.GetWord();
			yscale = xscale;			// Format 2.14 
		} 
		else if ( flags & WE_HAVE_AN_X_AND_Y_SCALE ) 
		{
			xscale = str.GetWord();
			yscale = str.GetWord();
		} 
		else if ( flags & WE_HAVE_A_TWO_BY_TWO ) 
		{
			xscale  = str.GetWord();
			scale01 = str.GetWord();
			scale10 = str.GetWord();
			yscale  = str.GetWord();
		}

		if ( flags & ARGS_ARE_XY_VALUES )
		{
			XFORM xm;

			xm.eDx  = (float) argument1;
			xm.eDy  = (float) argument2;
			xm.eM11 = xscale  / (float) 16384.0;
			xm.eM12 = scale01 / (float) 16384.0;
			xm.eM21 = scale10 / (float) 16384.0;
			xm.eM22 = yscale  / (float) 16384.0;
			
			len += DecodeGlyph(glyphIndex, curve, & xm);
		}
		else
			assert(false);
	} 
	while ( flags & MORE_COMPONENTS );

	if ( flags & WE_HAVE_INSTRUCTIONS )
	{
		unsigned numInstr = str.GetWord();

		for (unsigned i=0; i<numInstr; i++)
			str.GetByte();
	}

	// The purpose of USE_MY_METRICS is to force the lsb and rsb to take on a desired value. 
	// For example, an i-circumflex (Unicode 00ef) is often composed of the circumflex and a dotless-i. 
	// In order to force the composite to have the same metrics as the dotless-i, 
	// set USE_MY_METRICS for the dotless-i component of the composite. Without this bit, 
	// the rsb and lsb would be calculated from the HMTX entry for the composite (or would need to be 
	// explicitly set with TrueType instructions). 

	// Note that the behavior of the USE_MY_METRICS operation is undefined for rotated composite components.

	return len;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?