/// <summary>Used when delaying the loading of a glyph. This results in rapid startup.</summary>
		public static void LoadFully(Glyph glyph,FontParser parser,LoadMetaPoint meta){
			
			parser.Position=meta.Start;
			
			LoadGlyph(glyph,meta.Length,parser,glyph.Font.UnitsPerEmF);
			
		}
		public static void Load(FontParser parser,int offset,FontFace font,Glyph[] glyphs,int numMetrics){
			
			// Seek there:
			parser.Position=offset;
			
			// Get the glyph length:
			int numGlyphs=glyphs.Length;
		
			ushort advanceWidth=0;
			short leftSideBearing=0;
			
			// For each one..
			for (int i=0;i<numGlyphs;i++){
				
				// Monospaced fonts only have one entry:
				if (i<numMetrics){
					advanceWidth=parser.ReadUInt16();
					leftSideBearing=parser.ReadInt16();
				}
				
				Glyph glyph=glyphs[i];
				
				glyph.AdvanceWidth=(float)advanceWidth/font.UnitsPerEmF;
				glyph.LeftSideBearing=(float)leftSideBearing/font.UnitsPerEmF;
			}
			
			
		}
		private static Glyph[] LoadIndex(FontParser parser,CffGlyphParser cffParser){
			
			// Read the index which contains a bunch of char strings.
			// Each charstring is a postscript glyph definition.
			
			// How many are in here?
			int count=parser.ReadUInt16();
			
			if(count==0){
				
				return null;
				
			}
			
			// Create the offset set:
			int[] offsets=new int[count+1];
			
			// Read the offset size:
			int offsetSize=parser.ReadByte();
			
			// Read each offset:
			for(int i=0;i<=count;i++){
				
				// Read the current offset:
				offsets[i]=parser.ReadOffset(offsetSize);
				
			}
		
			// Grab the object offset, minus one as their not zero based:
			int objectOffset=parser.Position-1;
			
			// Create the glyph set:
			Glyph[] glyphs=new Glyph[offsets.Length-1];
			
			// For each one..
			for(int i=0;i<glyphs.Length;i++){
				
				// Get the (relative) indices:
				int startIndex=offsets[i];
				int length=offsets[i+1]-startIndex;
				
				// Load the glyph now, which starts at startIndex+objectOffset:
				Glyph glyph=cffParser.LoadGlyph(startIndex+objectOffset,length);
				
				// Add to the set:
				glyphs[i]=glyph;
				
			}
			
			// Seek over the table:
			parser.Position=objectOffset+offsets[count];
			
			return glyphs;
			
		}
		public void Reset(Glyph glyph){
			
			X=0f;
			Y=0f;
			NStems=0;
			HasWidth=false;
			Width=DefaultWidthX;
			Stack.Clear();
			
			Glyph=glyph;
		}
		public static void Load(FontParser parser,int offset,FontFace font,Glyph[] glyphs){
			
			// Seek:
			parser.Position=offset;
			
			// Check table version - 0 is current:
			if(parser.ReadUInt16()!=0){
				return;
			}
			
			// Skip ntables:
			parser.Position+=2;
			
			// Sub-table version - 0 is current:
			if(parser.ReadUInt16()!=0){
				return;
			}
			
			// Skip subTableLength, subTableCoverage:
			parser.Position+=4;
			
			// How many pairs?
			int pairCount=parser.ReadUInt16();
			
			// Skip searchRange, entrySelector, rangeShift.
			parser.Position+=6;
			
			float scaleFactor=1f/font.UnitsPerEmF;
			
			for(int i=0;i<pairCount;i++){
				
				// Get the glyph indices:
				int leftIndex=parser.ReadUInt16();
				int rightIndex=parser.ReadUInt16();
				
				// Get the kerning value:
				short value=parser.ReadInt16();
				
				// Push:
				Glyph right=glyphs[rightIndex];
				Glyph left=glyphs[leftIndex];
				
				if(right==null || left==null){
					continue;
				}
				
				right.AddKerningPair(left,(float)value * scaleFactor);
				
			}
			
		}
		public static Glyph[] Load(FontParser parser,int start,uint[] locations,FontFace font){
			
			// Get the vertical range of the font - it's the em size.
			float range=font.UnitsPerEmF;
			
			// The number of glyphs (last location is just used for computation purposes).
			int glyphCount=locations.Length-1;
			
			Glyph[] glyphs=new Glyph[glyphCount];
			font.ParserGlyphs=glyphs;
			
			// For each glyph..
			for(int i=0;i<glyphCount;i++){
				
				uint offset=locations[i];
				uint nextOffset=locations[i+1];
				
				if(offset!=nextOffset){
					
					// Seek there now:
					parser.Position=start+(int)offset;
					
					// Load it:
					glyphs[i]=ParseGlyph(parser,font,range);
					
				}else{
					
					glyphs[i]=new Glyph(font);
					
				}
				
			}
			
			if(Fonts.Preload){
				
				// Composite glyphs next.
				for(int i=0;i<glyphCount;i++){
					
					Glyph glyph=glyphs[i];
					
					if(glyph!=null){
						glyph.LoadFully(glyphs);
					}
					
				}
				
			}
			
			return glyphs;
			
		}
		public static void Load(FontParser parser,int offset,FontFace font,Glyph[] glyphs){
			
			// Seek:
			parser.Position=offset;
			
			//float scaleFactor=1f/font.UnitsPerEmF;
			
			// Look for kerning data.
			/*Glyph right=glyphs[rightIndex];
			Glyph left=glyphs[leftIndex];
			
			if(right==null || left==null){
				continue;
			}
			
			right.AddKerningPair(left,(float)value * scaleFactor);
			*/
			
		}
		public void LoadFully(Glyph glyph,LoadMetaPoint meta){
			
			// Reset this parser:
			Reset(glyph);
			
			// Parse now:
			Parse(meta.Start,meta.Length);
			
			// Apply width:
			glyph.AdvanceWidth=Width * ScaleRatio;
			
			// Close if we haven't already:
			glyph.ClosePath();
			
			if(Font.WindingUnknown){
				// Find the winding now:
				Font.FindWinding(glyph);
			}
			
		}
		public Glyph LoadGlyph(int start,int length){
			
			// Create our glyph:
			Glyph glyph=new Glyph(Font);
			
			if(FullLoad){
				
				// Reset this parser:
				Reset(glyph);
				
				// Parse now:
				Parse(start,length);
				
				// Apply width:
				glyph.AdvanceWidth=Width * ScaleRatio;
				
				// Close if we haven't already:
				glyph.ClosePath();
				
				if(Font.WindingUnknown){
					// Find the winding now:
					Font.FindWinding(glyph);
				}
				
			}else{
				
				// Increase unloaded count:
				Font.UnloadedGlyphs++;
				
				// Add position info:
				glyph.AddPathNode(new LoadMetaPoint(start,length));
				
			}
			
			return glyph;
		}
Exemple #10
0
        private static void LoadGlyph(Glyph glyph, int contourCount, FontParser parser, float range)
        {
            // The contour count (tiny set):
            ushort[] endPointIndices = new ushort[contourCount];

            // Load each endpoint:
            for (int i = 0; i < contourCount; i++)
            {
                endPointIndices[i] = parser.ReadUInt16();
            }

            // How big is the instruction block?
            int instructionLength = parser.ReadUInt16();

            // And skip it!
            parser.Position += instructionLength;

            // How many coordinates?
            int numberOfCoordinates = endPointIndices[endPointIndices.Length - 1] + 1;

            // Create the flag set:
            byte[] flags = new byte[numberOfCoordinates];

            // For each one..
            for (int i = 0; i < numberOfCoordinates; i++)
            {
                byte flag = parser.ReadByte();

                flags[i] = flag;

                // If bit 3 is set, we repeat this flag n times, where n is the next byte.
                if ((flag & 8) > 0)
                {
                    int repeatCount = parser.ReadByte();

                    for (int j = 0; j < repeatCount; j += 1)
                    {
                        i++;
                        flags[i] = flag;
                    }
                }
            }

            if (endPointIndices.Length > 0)
            {
                // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
                if (numberOfCoordinates > 0)
                {
                    // Current coord:
                    int coord = 0;

                    // Coord index:
                    int coordIndex = 0;

                    // The coord set:
                    float[] coords = new float[numberOfCoordinates * 2];

                    // Load X coords:
                    for (int i = 0; i < numberOfCoordinates; i++)
                    {
                        byte flag = flags[i];

                        coord = LoadGlyphCoordinate(parser, flag, coord, 2, 16);

                        coords[coordIndex] = (float)coord / range;

                        coordIndex += 2;
                    }

                    // Reset shared vars:
                    coord      = 0;
                    coordIndex = 1;

                    // Load Y coords:
                    for (int i = 0; i < numberOfCoordinates; i++)
                    {
                        byte flag = flags[i];

                        coord = LoadGlyphCoordinate(parser, flag, coord, 4, 32);

                        coords[coordIndex] = (float)coord / range;

                        coordIndex += 2;
                    }

                    int[] orderedEnds = new int[endPointIndices.Length];
                    int   currentEnd  = 0;

                    for (int i = 0; i < numberOfCoordinates; i++)
                    {
                        // Grab the flag:
                        byte flag = flags[i];

                        // On curve flag - Control point otherwise:
                        flag = (byte)(flag & 1);

                        // Last point of the current contour?

                        // For each end point index (tiny set - better than hash):
                        for (int e = endPointIndices.Length - 1; e >= 0; e--)
                        {
                            if (endPointIndices[e] == i)
                            {
                                orderedEnds[currentEnd] = i;

                                currentEnd++;

                                break;
                            }
                        }

                        // Update the flag - it's now just a 1 or 0:
                        flags[i] = flag;
                    }

                    // Reset shared index again:
                    coordIndex = 0;

                    // Create our temp holders of point info:
                    GlyphPoint firstPointRaw   = new GlyphPoint(flags, coords);
                    GlyphPoint lastPointRaw    = new GlyphPoint(flags, coords);
                    GlyphPoint prevPointRaw    = new GlyphPoint(flags, coords);
                    GlyphPoint currentPointRaw = new GlyphPoint(flags, coords);
                    GlyphPoint controlPoint    = new GlyphPoint(flags, coords);

                    // For each contour..
                    for (int i = 0; i < contourCount; i++)
                    {
                        int pointOffset = 0;

                        // Get the indices of the first/last points on this contour.
                        int firstIndex = 0;
                        int lastIndex  = orderedEnds[i];

                        if (i != 0)
                        {
                            firstIndex = orderedEnds[i - 1] + 1;
                        }

                        GlyphPoint firstPoint = firstPointRaw;
                        firstPoint.Set(firstIndex);

                        GlyphPoint lastPoint = lastPointRaw;
                        lastPoint.Set(lastIndex);

                        if (firstPoint.OnCurve)
                        {
                            // No control point:
                            controlPoint.Active = false;

                            // The first point will be consumed by the moveTo command so skip it:
                            pointOffset = 1;
                        }
                        else
                        {
                            if (lastPoint.OnCurve)
                            {
                                // If the first point is off-curve and the last point is on-curve,
                                // start at the last point.
                                firstPoint = lastPoint;
                            }
                            else
                            {
                                // If both first and last points are off-curve, start at their middle.

                                firstPoint.X = (firstPoint.X + lastPoint.X) / 2f;
                                firstPoint.Y = (firstPoint.Y + lastPoint.Y) / 2f;
                            }

                            controlPoint.Set(firstPoint);
                        }

                        glyph.MoveTo(firstPoint.X, firstPoint.Y);

                        int contourStart = firstIndex + pointOffset;

                        for (int j = contourStart; j <= lastIndex; j++)
                        {
                            // Setup the previous point:
                            GlyphPoint prevPoint;

                            if (j == firstIndex)
                            {
                                prevPoint = firstPoint;
                            }
                            else
                            {
                                prevPoint = prevPointRaw;
                                prevPoint.Set(j - 1);
                            }

                            // Setup the current point:
                            GlyphPoint pt = currentPointRaw;
                            pt.Set(j);

                            if (prevPoint.OnCurve && pt.OnCurve)
                            {
                                // Just a line here:
                                glyph.LineTo(pt.X, pt.Y);
                            }
                            else if (prevPoint.OnCurve && !pt.OnCurve)
                            {
                                controlPoint.Set(pt);
                            }
                            else if (!prevPoint.OnCurve && !pt.OnCurve)
                            {
                                float midPointX = (prevPoint.X + pt.X) / 2f;
                                float midPointY = (prevPoint.Y + pt.Y) / 2f;

                                glyph.QuadraticCurveTo(prevPoint.X, prevPoint.Y, midPointX, midPointY);
                                controlPoint.Set(pt);
                            }
                            else if (!prevPoint.OnCurve && pt.OnCurve)
                            {
                                // Previous point off-curve, this point on-curve.
                                glyph.QuadraticCurveTo(controlPoint.X, controlPoint.Y, pt.X, pt.Y);

                                controlPoint.Active = false;
                            }
                        }

                        if (firstPoint != lastPoint)
                        {
                            // Close the path.

                            if (controlPoint.Active)
                            {
                                // Still got a spare control point:
                                glyph.QuadraticCurveTo(controlPoint.X, controlPoint.Y, firstPoint.X, firstPoint.Y);
                            }

                            // Just a normal close:
                            glyph.ClosePath();
                        }
                    }
                }
            }

            if (glyph.Font.WindingUnknown)
            {
                // Find the winding now:
                glyph.Font.FindWinding(glyph);
            }
        }
		private static void LoadGlyph(Glyph glyph,int contourCount,FontParser parser,float range){
			
			// The contour count (tiny set):
			ushort[] endPointIndices=new ushort[contourCount];
			
			// Load each endpoint:
			for(int i=0;i<contourCount;i++){
				
				endPointIndices[i]=parser.ReadUInt16();
				
			}
			
			// How big is the instruction block?
			int instructionLength=parser.ReadUInt16();
			
			// And skip it!
			parser.Position+=instructionLength;
			
			// How many coordinates?
			int numberOfCoordinates=endPointIndices[endPointIndices.Length-1]+1;
			
			// Create the flag set:
			byte[] flags=new byte[numberOfCoordinates];
			
			// For each one..
			for (int i = 0; i < numberOfCoordinates;i++) {
				
				byte flag=parser.ReadByte();
				
				flags[i]=flag;
				
				// If bit 3 is set, we repeat this flag n times, where n is the next byte.
				if((flag&8)>0){
					int repeatCount = parser.ReadByte();
					
					for (int j = 0; j < repeatCount; j += 1) {
						i++;
						flags[i]=flag;
					}
				}
				
			}
			
			if (endPointIndices.Length > 0) {
				
				// X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
				if (numberOfCoordinates > 0){
					
					// Current coord:
					int coord=0;
					
					// Coord index:
					int coordIndex=0;
					
					// The coord set:
					float[] coords=new float[numberOfCoordinates*2];
					
					// Load X coords:
					for (int i = 0; i < numberOfCoordinates; i++) {
						byte flag = flags[i];
						
						coord = LoadGlyphCoordinate(parser,flag,coord,2,16);
						
						coords[coordIndex]=(float)coord/range;
						
						coordIndex+=2;
						
					}
					
					// Reset shared vars:
					coord=0;
					coordIndex=1;
					
					// Load Y coords:
					for (int i = 0; i < numberOfCoordinates; i++) {
						byte flag = flags[i];
						
						coord = LoadGlyphCoordinate(parser,flag,coord,4,32);
						
						coords[coordIndex]=(float)coord/range;
						
						coordIndex+=2;
						
					}
					
					int[] orderedEnds=new int[endPointIndices.Length];
					int currentEnd=0;
					
					for (int i = 0; i < numberOfCoordinates; i++) {
						
						// Grab the flag:
						byte flag=flags[i];
						
						// On curve flag - Control point otherwise:
						flag=(byte)(flag&1);
						
						// Last point of the current contour?
						
						// For each end point index (tiny set - better than hash):
						for(int e=endPointIndices.Length-1;e>=0;e--){
							
							if(endPointIndices[e]==i){
								
								orderedEnds[currentEnd]=i;
								
								currentEnd++;
								
								break;
							}
							
						}
						
						// Update the flag - it's now just a 1 or 0:
						flags[i]=flag;
						
					}
					
					// Reset shared index again:
					coordIndex=0;
					
					// Create our temp holders of point info:
					GlyphPoint firstPointRaw=new GlyphPoint(flags,coords);
					GlyphPoint lastPointRaw=new GlyphPoint(flags,coords);
					GlyphPoint prevPointRaw=new GlyphPoint(flags,coords);
					GlyphPoint currentPointRaw=new GlyphPoint(flags,coords);
					GlyphPoint controlPoint=new GlyphPoint(flags,coords);
					
					// For each contour..
					for(int i=0;i<contourCount;i++){
						
						int pointOffset=0;
						
						// Get the indices of the first/last points on this contour.
						int firstIndex=0;
						int lastIndex=orderedEnds[i];
						
						if(i!=0){
							firstIndex=orderedEnds[i-1]+1;
						}
						
						GlyphPoint firstPoint=firstPointRaw;
						firstPoint.Set(firstIndex);
						
						GlyphPoint lastPoint=lastPointRaw;
						lastPoint.Set(lastIndex);
						
						if(firstPoint.OnCurve){
							
							// No control point:
							controlPoint.Active=false;
							
							// The first point will be consumed by the moveTo command so skip it:
							pointOffset=1;
							
						}else{
							
							if(lastPoint.OnCurve){
								
								// If the first point is off-curve and the last point is on-curve,
								// start at the last point.
								firstPoint=lastPoint;
								
							}else{
								// If both first and last points are off-curve, start at their middle.
								
								firstPoint.X=(firstPoint.X+lastPoint.X)/2f;
								firstPoint.Y=(firstPoint.Y+lastPoint.Y)/2f;
								
							}
							
							controlPoint.Set(firstPoint);
							
						}
						
						glyph.MoveTo(firstPoint.X,firstPoint.Y);
						
						int contourStart=firstIndex+pointOffset;
						
						for(int j=contourStart;j<=lastIndex;j++){
							
							// Setup the previous point:
							GlyphPoint prevPoint;
							
							if(j==firstIndex){
								prevPoint=firstPoint;
							}else{
								prevPoint=prevPointRaw;
								prevPoint.Set(j-1);
							}
							
							// Setup the current point:
							GlyphPoint pt=currentPointRaw;
							pt.Set(j);
							
							if(prevPoint.OnCurve && pt.OnCurve) {
								
								// Just a line here:
								glyph.LineTo(pt.X,pt.Y);
								
							}else if (prevPoint.OnCurve && !pt.OnCurve){
								
								controlPoint.Set(pt);
								
							}else if(!prevPoint.OnCurve && !pt.OnCurve){
								
								float midPointX=(prevPoint.X+pt.X)/2f;
								float midPointY=(prevPoint.Y+pt.Y)/2f;
								
								glyph.QuadraticCurveTo(prevPoint.X,prevPoint.Y,midPointX,midPointY);
								controlPoint.Set(pt);
								
							}else if(!prevPoint.OnCurve && pt.OnCurve){
								
								// Previous point off-curve, this point on-curve.
								glyph.QuadraticCurveTo(controlPoint.X,controlPoint.Y,pt.X,pt.Y);
								
								controlPoint.Active=false;
								
							}
							
						}
						
						if(firstPoint!=lastPoint){
							// Close the path.
							
							if(controlPoint.Active){
								
								// Still got a spare control point:
								glyph.QuadraticCurveTo(controlPoint.X,controlPoint.Y,firstPoint.X,firstPoint.Y);
								
							}
							
							// Just a normal close:
							glyph.ClosePath();
							
						}
						
					}
					
				}
				
			}
			
			if(glyph.Font.WindingUnknown){
				// Find the winding now:
				glyph.Font.FindWinding(glyph);
			}
			
		}
Exemple #12
0
        public static bool Load(FontParser parser, int start, FontFace font, Glyph[] glyphs)
        {
            // Seek there:
            parser.Position = start;

            // Read the version (and check if it's zero):
            if (parser.ReadUInt16() != 0)
            {
                return(false);
            }

            // Strangely the cmap table can have lots of tables inside it. For now we're looking for the common type 3 table.

            // Number of tables:
            int tableCount = parser.ReadUInt16();

            // Total characters in font:
            int characterCount = 0;

            int offset = -1;
            int favour = 0;

            for (int i = 0; i < tableCount; i += 1)
            {
                // Grab the platform ID:
                int platformId = parser.ReadUInt16();

                // And the encoding ID:
                int encodingId = parser.ReadUInt16();

                if (platformId == 3 || platformId == 0)
                {
                    if (encodingId == 10)
                    {
                        // Top favourite - most broad Unicode encoding.

                        // Read offset:
                        offset = (int)parser.ReadUInt32();

                        break;
                    }
                    else if (encodingId == 1 || encodingId == 0)
                    {
                        // Read offset:
                        offset = (int)parser.ReadUInt32();

                        // Mid-range favourite:
                        favour = 1;

                        continue;
                    }
                    else if (favour == 0)
                    {
                        // Anything else we'll give a try:

                        // Read offset (but don't break):
                        offset = (int)parser.ReadUInt32();

                        continue;
                    }
                }

                // Skip offset:
                parser.Position += 4;
            }

            if (offset == -1)
            {
                // We don't support this font :(
                return(false);
            }

            // Seek to the cmap now:
            parser.Position = start + offset;

            // Check it's format 4:
            int format = parser.ReadUInt16();

            if (format > 6)
            {
                // We now have e.g. 12.0 - another short here:
                parser.Position += 2;

                // Size/ structure are both 4 byte ints now:
                parser.Position += 8;
            }
            else
            {
                // Size of the sub-table (map length, u16):
                parser.Position += 2;

                // Structure of the sub-table (map language, u16):
                parser.Position += 2;
            }

            switch (format)
            {
            case 0:

                // Byte encoding table:

                for (int i = 0; i < 256; i++)
                {
                    int rByte = parser.ReadByte();

                    Glyph glyph = glyphs[rByte];

                    if (glyph != null)
                    {
                        characterCount++;

                        glyph.AddCharcode(i);
                    }
                }

                break;

            case 2:

                // The offset to the headers:
                int subOffset = parser.Position + (256 * 2);

                // For each high byte:
                for (int i = 0; i < 256; i++)
                {
                    // Read the index to the header and zero out the bottom 3 bits:
                    int headerPosition = subOffset + (parser.ReadUInt16() & (~7));

                    // Read the header:
                    int   firstCode  = parser.ReadUInt16(ref headerPosition);
                    int   entryCount = parser.ReadUInt16(ref headerPosition);
                    short idDelta    = parser.ReadInt16(ref headerPosition);

                    // Grab the current position:
                    int pos = headerPosition;

                    // Read the idRangeOffset - the last part of the header:
                    pos += parser.ReadUInt16(ref headerPosition);

                    int maxCode = firstCode + entryCount;

                    // Get the high byte value:
                    int highByte = (i << 8);

                    // For each low byte:
                    for (int j = firstCode; j < maxCode; j++)
                    {
                        // Get the full charcode (which might not actually exist yet):
                        int charCode = highByte + j;

                        // Read the base of the glyphIndex:
                        int p = parser.ReadUInt16(ref pos);

                        if (p == 0)
                        {
                            continue;
                        }

                        p = (p + idDelta) & 0xFFFF;

                        if (p == 0)
                        {
                            continue;
                        }

                        Glyph glyph = glyphs[p];

                        if (glyph != null)
                        {
                            characterCount++;

                            glyph.AddCharcode(charCode);
                        }
                    }
                }

                break;

            case 4:

                // Segment count. It's doubled.
                int segCount = (parser.ReadUInt16() >> 1);

                // Search range, entry selector and range shift (don't need any):
                parser.Position += 6;

                int baseIndex = parser.Position;

                int endCountIndex = baseIndex;

                baseIndex += 2;

                int startCountIndex    = baseIndex + segCount * 2;
                int idDeltaIndex       = baseIndex + segCount * 4;
                int idRangeOffsetIndex = baseIndex + segCount * 6;

                for (int i = 0; i < segCount - 1; i++)
                {
                    int endCount      = parser.ReadUInt16(ref endCountIndex);
                    int startCount    = parser.ReadUInt16(ref startCountIndex);
                    int idDelta       = parser.ReadInt16(ref idDeltaIndex);
                    int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex);

                    for (int c = startCount; c <= endCount; c++)
                    {
                        int glyphIndex;

                        if (idRangeOffset != 0)
                        {
                            // The idRangeOffset is relative to the current position in the idRangeOffset array.
                            // Take the current offset in the idRangeOffset array.
                            int glyphIndexOffset = (idRangeOffsetIndex - 2);

                            // Add the value of the idRangeOffset, which will move us into the glyphIndex array.
                            glyphIndexOffset += idRangeOffset;

                            // Then add the character index of the current segment, multiplied by 2 for USHORTs.
                            glyphIndexOffset += (c - startCount) * 2;

                            glyphIndex = parser.ReadUInt16(ref glyphIndexOffset);

                            if (glyphIndex != 0)
                            {
                                glyphIndex = (glyphIndex + idDelta) & 0xFFFF;
                            }
                        }
                        else
                        {
                            glyphIndex = (c + idDelta) & 0xFFFF;
                        }

                        // Add a charcode to the glyph now:
                        Glyph glyph = glyphs[glyphIndex];

                        if (glyph != null)
                        {
                            characterCount++;

                            glyph.AddCharcode(c);
                        }
                    }
                }

                break;

            case 6:

                int firstCCode  = parser.ReadUInt16();
                int entryCCount = parser.ReadUInt16();

                for (int i = 0; i < entryCCount; i++)
                {
                    Glyph glyphC = glyphs[parser.ReadUInt16()];

                    if (glyphC != null)
                    {
                        characterCount++;

                        glyphC.AddCharcode(firstCCode + i);
                    }
                }

                break;

            case 12:

                int groups = (int)parser.ReadUInt32();

                for (int i = 0; i < groups; i++)
                {
                    int startCode  = (int)parser.ReadUInt32();
                    int endCode    = (int)parser.ReadUInt32();
                    int startGlyph = (int)parser.ReadUInt32();

                    int count = (endCode - startCode);

                    for (int j = 0; j <= count; j++)
                    {
                        int glyphIndex = (startGlyph + j);

                        Glyph glyph = glyphs[glyphIndex];

                        if (glyph != null)
                        {
                            characterCount++;

                            glyph.AddCharcode(startCode + j);
                        }
                    }
                }

                break;

            default:
                Fonts.OnLogMessage("InfiniText does not currently support this font. If you need it, please contact us with this: Format: " + format);
                break;
            }

            font.CharacterCount = characterCount;

            return(true);
        }
		public static Glyph ParseGlyph(FontParser parser,FontFace font,float range){
			
			// How many contours has it got?
			int contourCount=parser.ReadInt16();
			
			// Skip bounds - we don't trust these too much, so we'll figure them out ourselves:
			parser.Position+=8;
			
			if(contourCount>0){
				// This glyph is not a composite.
				
				// Create the glyph:
				Glyph glyph=new Glyph(font);
				
				if(Fonts.Preload){
					
					LoadGlyph(glyph,contourCount,parser,range);
					
				}else{
					
					// Increase unloaded count:
					font.UnloadedGlyphs++;
					
					// Add position info:
					glyph.AddPathNode(new LoadMetaPoint(parser.Position,contourCount));
					
				}
				
				return glyph;
				
			}else if(contourCount==0){
				
				// Empty glyph e.g. space. Create the glyph:
				Glyph glyph=new Glyph(font);
				
				return glyph;
				
			}
			
			CompositeGlyph compGlyph=new CompositeGlyph(font);
			
			bool moreComponents=true;
			
			while(moreComponents){
			
				ushort cFlags=parser.ReadUInt16();
				ushort glyphIndex=parser.ReadUInt16();
				
				VectorTransform component=new VectorTransform(glyphIndex);
				
				if ((cFlags & 1) > 0) {
					// The arguments are words
					component.Dx = (float)parser.ReadInt16() / range;
					component.Dy = (float)parser.ReadInt16() / range;
				} else {
					// The arguments are bytes
					component.Dx = (float)parser.ReadByte()  / range;
					component.Dy = (float)parser.ReadByte()  / range;
				}
				
				if ((cFlags & 8) > 0) {
					// We have one scale
					component.XScale = component.YScale = parser.ReadF2Dot14();
				} else if ((cFlags & 64) > 0) {
					// We have an X / Y scale
					component.XScale = parser.ReadF2Dot14();
					component.YScale = parser.ReadF2Dot14();
				} else if ((cFlags & 128) > 0) {
					// We have a 2x2 transformation
					component.XScale = parser.ReadF2Dot14();
					component.Scale01 = parser.ReadF2Dot14();
					component.Scale10 = parser.ReadF2Dot14();
					component.YScale = parser.ReadF2Dot14();
				}
				
				// Push the component to the end:
				compGlyph.AddComponent(component);
				
				moreComponents = ((cFlags & 32)==32);
			}
			
			return compGlyph;
			
		}
//--------------------------------------
		public static bool Load(FontParser parser,int start,FontFace font,Glyph[] glyphs){
			
			// Seek there:
			parser.Position=start;
			
			// Read the version (and check if it's zero):
			if(parser.ReadUInt16()!=0){
				return false;
			}
			
			// Strangely the cmap table can have lots of tables inside it. For now we're looking for the common type 3 table.
			
			// Number of tables:
			int tableCount=parser.ReadUInt16();
			
			// Total characters in font:
			int characterCount=0;
			
			int offset=-1;
			int favour=0;
			
			for(int i = 0; i < tableCount; i += 1) {
				
				// Grab the platform ID:
				int platformId=parser.ReadUInt16();
				
				// And the encoding ID:
				int encodingId=parser.ReadUInt16();
				
				if(platformId==3 || platformId==0){
					
					if(encodingId==10){
						
						// Top favourite - most broad Unicode encoding.
						
						// Read offset:
						offset=(int)parser.ReadUInt32();
						
						break;
						
					}else if(encodingId==1 || encodingId==0){
						
						// Read offset:
						offset=(int)parser.ReadUInt32();
						
						// Mid-range favourite:
						favour=1;
						
						continue;
						
					}else if(favour==0){
						
						// Anything else we'll give a try:
						
						// Read offset (but don't break):
						offset=(int)parser.ReadUInt32();
						
						continue;
						
					}
					
				}
				
				// Skip offset:
				parser.Position+=4;
				
			}
			
			if(offset==-1){
				// We don't support this font :(
				return false;
			}
			
			// Seek to the cmap now:
			parser.Position=start+offset;
			
			// Check it's format 4:
			int format=parser.ReadUInt16();
		
			if(format>6){
				// We now have e.g. 12.0 - another short here:
				parser.Position+=2;
				
				// Size/ structure are both 4 byte ints now:
				parser.Position+=8;
				
			}else{
			
				// Size of the sub-table (map length, u16):
				parser.Position+=2;
				
				// Structure of the sub-table (map language, u16):
				parser.Position+=2;
				
			}
			
			switch(format){
				
				case 0:
				
				// Byte encoding table:
				
				for(int i=0;i<256;i++){
					
					int rByte=parser.ReadByte();
					
					Glyph glyph=glyphs[rByte];
					
					if(glyph!=null){
						characterCount++;
					
						glyph.AddCharcode(i);
					}
					
				}
				
				break;
				case 2:
				
				// The offset to the headers:
				int subOffset=parser.Position + (256 * 2);
				
				// For each high byte:
				for(int i=0;i<256;i++){
					
					// Read the index to the header and zero out the bottom 3 bits:
					int headerPosition=subOffset + (parser.ReadUInt16() & (~7));
					
					// Read the header:
					int firstCode=parser.ReadUInt16(ref headerPosition);
					int entryCount=parser.ReadUInt16(ref headerPosition);
					short idDelta=parser.ReadInt16(ref headerPosition);
					
					// Grab the current position:
					int pos=headerPosition;
					
					// Read the idRangeOffset - the last part of the header:
					pos+=parser.ReadUInt16(ref headerPosition);
					
					int maxCode=firstCode+entryCount;
					
					// Get the high byte value:
					int highByte=(i<<8);
					
					// For each low byte:
					for (int j=firstCode;j<maxCode;j++){
						
						// Get the full charcode (which might not actually exist yet):
						int charCode=highByte+j;
						
						// Read the base of the glyphIndex:
						int p=parser.ReadUInt16(ref pos);
						
						if(p==0){
							continue;
						}
						
						p=(p+idDelta) & 0xFFFF;
						
						if(p==0){
							continue;
						}
						
						Glyph glyph=glyphs[p];
						
						if(glyph!=null){
							
							characterCount++;
							
							glyph.AddCharcode(charCode);
							
						}
						
					}
				}
				
				break;
				case 4:
				
				// Segment count. It's doubled.
				int segCount=(parser.ReadUInt16() >> 1);
				
				// Search range, entry selector and range shift (don't need any):
				parser.Position+=6;
				
				int baseIndex=parser.Position;
				
				int endCountIndex=baseIndex;
				
				baseIndex+=2;
				
				int startCountIndex = baseIndex + segCount * 2;
				int idDeltaIndex = baseIndex + segCount * 4;
				int idRangeOffsetIndex = baseIndex + segCount * 6;
				
				for(int i = 0; i < segCount - 1; i ++){
					
					int endCount = parser.ReadUInt16(ref endCountIndex);
					int startCount = parser.ReadUInt16(ref startCountIndex);
					int idDelta = parser.ReadInt16(ref idDeltaIndex);
					int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex);
					
					for(int c = startCount; c <= endCount;c++){
						
						int glyphIndex;
						
						if(idRangeOffset != 0){
							
							// The idRangeOffset is relative to the current position in the idRangeOffset array.
							// Take the current offset in the idRangeOffset array.
							int glyphIndexOffset = (idRangeOffsetIndex - 2);
							
							// Add the value of the idRangeOffset, which will move us into the glyphIndex array.
							glyphIndexOffset += idRangeOffset;
							
							// Then add the character index of the current segment, multiplied by 2 for USHORTs.
							glyphIndexOffset += (c - startCount) * 2;
							
							glyphIndex=parser.ReadUInt16(ref glyphIndexOffset);
							
							if(glyphIndex!=0){
								glyphIndex = (glyphIndex + idDelta) & 0xFFFF;
							}
							
						}else{
							glyphIndex = (c + idDelta) & 0xFFFF;
						}
						
						// Add a charcode to the glyph now:
						Glyph glyph=glyphs[glyphIndex];
						
						if(glyph!=null){
							characterCount++;
						
							glyph.AddCharcode(c);
						}
					}
					
				}
				
				break;
				
				case 6:
				
				int firstCCode=parser.ReadUInt16();
				int entryCCount=parser.ReadUInt16();
				
				for(int i=0;i<entryCCount;i++){
					
					Glyph glyphC=glyphs[parser.ReadUInt16()];
					
					if(glyphC!=null){
					
						characterCount++;
						
						glyphC.AddCharcode(firstCCode+i);
					
					}
					
				}
				
				break;
				
				case 12:
				
				int groups=(int)parser.ReadUInt32();
				
				for(int i=0;i<groups;i++){
					int startCode=(int)parser.ReadUInt32();
					int endCode=(int)parser.ReadUInt32();
					int startGlyph=(int)parser.ReadUInt32();
					
					int count=(endCode - startCode);
					
					for(int j=0;j<=count;j++){
						
						int glyphIndex=(startGlyph+j);
						
						Glyph glyph=glyphs[glyphIndex];
						
						if(glyph!=null){
							
							characterCount++;
							
							glyph.AddCharcode(startCode+j);
							
						}
						
					}
				}
				
				break;
				
				default:
					Fonts.OnLogMessage("InfiniText does not currently support this font. If you need it, please contact us with this: Format: "+format);
				break;
			}
			
			font.CharacterCount=characterCount;
			
			return true;
			
		}
Exemple #16
0
        /// <summary>Used when delaying the loading of a glyph. This results in rapid startup.</summary>
        public static void LoadFully(Glyph glyph, FontParser parser, LoadMetaPoint meta)
        {
            parser.Position = meta.Start;

            LoadGlyph(glyph, meta.Length, parser, glyph.Font.UnitsPerEmF);
        }
//--------------------------------------
		private void TransformPoints(Glyph fromGlyph,VectorTransform transform){
			VectorPoint current=fromGlyph.FirstPathNode;
			
			while(current!=null){
				
				// Create a new one:
				VectorPoint newPoint = current.Copy();
				
				// Apply transformed pos:
				newPoint.Transform(transform);
				
				// Add it:
				AddPathNode(newPoint);
				
				current=current.Next;
				
			}
			
		}
//--------------------------------------
//--------------------------------------
		public override void LoadFully(Glyph[] glyphs){
			
			VectorTransform current=FirstComponent;
			
			while(current!=null){
				
				Glyph componentGlyph=glyphs[current.Index];
				
				if(componentGlyph!=null){
					
					// Transform the points of the component glyph into this one: 
					TransformPoints(componentGlyph,current);
					
				}
				
				current=current.Next;
				
			}
			
		}
Exemple #22
0
        private static int LoadSubTable(FontParser parser, int start, FontFace font, Glyph[] glyphs)
        {
            // Total characters in subtable:
            int characterCount = 0;

            // Seek to the cmap now:
            parser.Position = start;

            // Check it's format 4:
            int format = parser.ReadUInt16();

                        #if INFINITEXT_DEBUG
            Fonts.OnLogMessage("Cmap subtable format: " + format);
                        #endif

            if (format > 13)
            {
                // We now have e.g. 14.0 - ulong here ("Length"):
                parser.Position += 4;
            }
            else if (format > 6)
            {
                // We now have e.g. 12.0 - another short here (reserved):
                parser.Position += 2;

                // Length and language are both 4 byte ints now:
                parser.Position += 8;
            }
            else
            {
                // Size of the sub-table (map length, u16):
                parser.Position += 2;

                // Structure of the sub-table (map language, u16):
                parser.Position += 2;
            }

            switch (format)
            {
            case 0:

                // Byte encoding table:

                for (int i = 0; i < 256; i++)
                {
                    int rByte = parser.ReadByte();

                    Glyph glyph = glyphs[rByte];

                    if (glyph != null)
                    {
                        characterCount++;

                        glyph.AddCharcode(i);
                    }
                }

                break;

            case 2:

                // The offset to the headers:
                int subOffset = parser.Position + (256 * 2);

                // For each high byte:
                for (int i = 0; i < 256; i++)
                {
                    // Read the index to the header and zero out the bottom 3 bits:
                    int headerPosition = subOffset + (parser.ReadUInt16() & (~7));

                    // Read the header:
                    int   firstCode  = parser.ReadUInt16(ref headerPosition);
                    int   entryCount = parser.ReadUInt16(ref headerPosition);
                    short idDelta    = parser.ReadInt16(ref headerPosition);

                    // Grab the current position:
                    int pos = headerPosition;

                    // Read the idRangeOffset - the last part of the header:
                    pos += parser.ReadUInt16(ref headerPosition);

                    int maxCode = firstCode + entryCount;

                    // Get the high byte value:
                    int highByte = (i << 8);

                    // For each low byte:
                    for (int j = firstCode; j < maxCode; j++)
                    {
                        // Get the full charcode (which might not actually exist yet):
                        int charCode = highByte + j;

                        // Read the base of the glyphIndex:
                        int p = parser.ReadUInt16(ref pos);

                        if (p == 0)
                        {
                            continue;
                        }

                        p = (p + idDelta) & 0xFFFF;

                        if (p == 0)
                        {
                            continue;
                        }

                        Glyph glyph = glyphs[p];

                        if (glyph != null)
                        {
                            characterCount++;

                            glyph.AddCharcode(charCode);
                        }
                    }
                }

                break;

            case 4:

                // Segment count. It's doubled.
                int segCount = (parser.ReadUInt16() >> 1);

                // Search range, entry selector and range shift (don't need any):
                parser.Position += 6;

                int baseIndex = parser.Position;

                int endCountIndex = baseIndex;

                baseIndex += 2;

                int startCountIndex    = baseIndex + segCount * 2;
                int idDeltaIndex       = baseIndex + segCount * 4;
                int idRangeOffsetIndex = baseIndex + segCount * 6;

                for (int i = 0; i < segCount - 1; i++)
                {
                    int endCount      = parser.ReadUInt16(ref endCountIndex);
                    int startCount    = parser.ReadUInt16(ref startCountIndex);
                    int idDelta       = parser.ReadInt16(ref idDeltaIndex);
                    int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex);

                    for (int c = startCount; c <= endCount; c++)
                    {
                        int glyphIndex;

                        if (idRangeOffset != 0)
                        {
                            // The idRangeOffset is relative to the current position in the idRangeOffset array.
                            // Take the current offset in the idRangeOffset array.
                            int glyphIndexOffset = (idRangeOffsetIndex - 2);

                            // Add the value of the idRangeOffset, which will move us into the glyphIndex array.
                            glyphIndexOffset += idRangeOffset;

                            // Then add the character index of the current segment, multiplied by 2 for USHORTs.
                            glyphIndexOffset += (c - startCount) * 2;

                            glyphIndex = parser.ReadUInt16(ref glyphIndexOffset);

                            if (glyphIndex != 0)
                            {
                                glyphIndex = (glyphIndex + idDelta) & 0xFFFF;
                            }
                        }
                        else
                        {
                            glyphIndex = (c + idDelta) & 0xFFFF;
                        }

                        // Add a charcode to the glyph now:
                        Glyph glyph = glyphs[glyphIndex];

                        if (glyph != null)
                        {
                            characterCount++;

                            glyph.AddCharcode(c);
                        }
                    }
                }

                break;

            case 6:

                int firstCCode  = parser.ReadUInt16();
                int entryCCount = parser.ReadUInt16();

                for (int i = 0; i < entryCCount; i++)
                {
                    Glyph glyphC = glyphs[parser.ReadUInt16()];

                    if (glyphC != null)
                    {
                        characterCount++;

                        glyphC.AddCharcode(firstCCode + i);
                    }
                }

                break;

            case 10:

                // Trimmed array. Similar to format 6.

                int startCharCode = parser.ReadUInt16();
                int numChars      = parser.ReadUInt16();

                for (int i = 0; i < numChars; i++)
                {
                    Glyph glyphC = glyphs[parser.ReadUInt16()];

                    if (glyphC != null)
                    {
                        characterCount++;

                        glyphC.AddCharcode(startCharCode + i);
                    }
                }

                break;

            case 12:

                // Segmented coverage.
                // Mapping of 1 charcode to 1 glyph. "Segmented" because it can come in blocks called groups.

                int groups = (int)parser.ReadUInt32();

                // For each group of glyphs..
                for (int i = 0; i < groups; i++)
                {
                    // Start/end charcode:
                    int startCode = (int)parser.ReadUInt32();
                    int endCode   = (int)parser.ReadUInt32();

                    // Start glyph ID:
                    int startGlyph = (int)parser.ReadUInt32();

                    int count = (endCode - startCode);

                    // For each glyph/charcode pair..
                    for (int j = 0; j <= count; j++)
                    {
                        // Get the glyph:
                        int glyphIndex = (startGlyph + j);

                        Glyph glyph = glyphs[glyphIndex];

                        if (glyph != null)
                        {
                            characterCount++;

                            // Charcode is..
                            glyph.AddCharcode(startCode + j);
                        }
                    }
                }

                break;

            case 13:

                // Many to one. Same format as #12 but the meaning is a bit different.

                // How many groups?
                int glyphCount = (int)parser.ReadUInt32();

                for (int i = 0; i < glyphCount; i++)
                {
                    int startCode = (int)parser.ReadUInt32();
                    int endCode   = (int)parser.ReadUInt32();
                    int glyphID   = (int)parser.ReadUInt32();

                    // Get the glyph:
                    Glyph glyph = glyphs[glyphID];

                    if (glyph != null)
                    {
                        int count = (endCode - startCode);

                        // For each charcode..
                        for (int j = 0; j <= count; j++)
                        {
                            characterCount++;

                            // Hook up glyph to this charcode:
                            glyph.AddCharcode(startCode + j);
                        }
                    }
                }

                break;

            case 14:

                Fonts.OnLogMessage("InfiniText partially supports part of the font '" + font.Family.Name + "' - this is harmless. Search for this message for more.");

                // This font contains a format 14 CMAP Table.
                // Format 14 is "Unicode variation selectors" - essentially different versions of the same character.
                // E.g. a text Emoji character and a graphical one.
                // In a text system like InfiniText, that just means we must map a bunch of different charcodes
                // to the same glyph.

                // .. I Think! As usual, the OpenType spec doesn't make too much sense.
                // However, it appears to be entirely optional.

                // So, approx implementation is below, however getting the utf32 code point from the variation + base character
                // is completely undocumented - my best guess unfortunately threw errors.

                // See the commented out block below!

                break;

            /*
             *
             * case 14:
             *
             * // How many var selector records?
             * int records=(int)parser.ReadUInt32();
             *
             * for(int i=0;i<records;i++){
             *
             *      // variation selector:
             *      int varSelector=(int)parser.ReadUInt24();
             *
             *      // Offsets:
             *      int defaultUVSOffset=(int)parser.ReadUInt32();
             *      int nonDefaultUVSOffset=(int)parser.ReadUInt32();
             *
             *      // Grab parser position:
             *      int position=parser.Position;
             *
             *      // Got a ref to a default style table?
             *      if(defaultUVSOffset!=0){
             *
             *              // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes.
             *              // [BaseCharCode][The extended part. Each of these comes from the range.]
             *              // The actual glyph is the one that we get by directly looking up each of the base characters.
             *
             *              // Seek to the table:
             *              parser.Position=start+defaultUVSOffset;
             *
             *              // Read the unicode value ranges count:
             *              int numUniRangesCount=(int)parser.ReadUInt32();
             *
             *              // For each one..
             *              for(int m=0;m<numUniRangesCount;m++){
             *
             *                      // Read the base charcode:
             *                      int baseCharcode=(int)parser.ReadUInt24();
             *
             *                      // Read the size of the range:
             *                      byte rangeSize=parser.ReadByte();
             *
             *                      for(int c=0;c<=rangeSize;c++){
             *
             *                              // Fetch the base glyph:
             *                              Glyph glyph=font.GetGlyphDirect(baseCharcode);
             *
             *                              if(glyph!=null){
             *
             *                                      // Combine baseCharcode with varSelector next to form the variation (of "glyph").
             *
             *                                      // Get the full charcode (this is incorrect!):
             *                                      // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector);
             *
             *                                      // Add:
             *                                      //glyph.AddCharcode(charcode);
             *
             *                              }
             *
             *                              // Move baseCharcode along:
             *                              baseCharcode++;
             *
             *                      }
             *
             *              }
             *
             *              // Restore parser:
             *              parser.Position=position;
             *
             *      }
             *
             *      // Got a ref to a non-default style table?
             *      if(nonDefaultUVSOffset!=0){
             *
             *              // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes.
             *              // [BaseCharCode][The extended part. Each of these comes from the range.]
             *              // This time though, the glyph to use is directly specified
             *              // (that's what gives it the "non-default" property).
             *
             *              // Seek to the table:
             *              parser.Position=start+nonDefaultUVSOffset;
             *
             *              // Read the number of mappings:
             *              int numMappings=(int)parser.ReadUInt32();
             *
             *              // For each one..
             *              for(int m=0;m<numMappings;m++){
             *
             *                      // Read the base charcode:
             *                      int baseCharcode=(int)parser.ReadUInt24();
             *
             *                      // Read glyph ID:
             *                      int glyphID=(int)parser.ReadUInt16();
             *
             *                      // Get the glyph:
             *                      Glyph glyph=glyphs[glyphID];
             *
             *                      if(glyph!=null){
             *
             *                              // Combine baseCharcode with varSelector next to form the variation (of "glyph").
             *
             *                              // Get the full charcode (this is incorrect!):
             *                              // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector);
             *
             *                              // Add:
             *                              //glyph.AddCharcode(charcode);
             *
             *                      }
             *
             *              }
             *
             *              // Restore parser:
             *              parser.Position=position;
             *
             *      }
             *
             * }
             *
             * break;
             *
             */

            default:
                Fonts.OnLogMessage("InfiniText does not currently support part of this font. If you need it, please contact us with this: Format: " + format);
                break;
            }

            return(characterCount);
        }
//--------------------------------------
Exemple #24
0
        public static Glyph ParseGlyph(FontParser parser, FontFace font, float range)
        {
            // How many contours has it got?
            int contourCount = parser.ReadInt16();

            // Skip bounds - we don't trust these too much, so we'll figure them out ourselves:
            parser.Position += 8;

            if (contourCount > 0)
            {
                // This glyph is not a composite.

                // Create the glyph:
                Glyph glyph = new Glyph(font);

                if (Fonts.Preload)
                {
                    LoadGlyph(glyph, contourCount, parser, range);
                }
                else
                {
                    // Increase unloaded count:
                    font.UnloadedGlyphs++;

                    // Add position info:
                    glyph.AddPathNode(new LoadMetaPoint(parser.Position, contourCount));
                }

                return(glyph);
            }
            else if (contourCount == 0)
            {
                // Empty glyph e.g. space. Create the glyph:
                Glyph glyph = new Glyph(font);

                return(glyph);
            }

            CompositeGlyph compGlyph = new CompositeGlyph(font);

            bool moreComponents = true;

            while (moreComponents)
            {
                ushort cFlags     = parser.ReadUInt16();
                ushort glyphIndex = parser.ReadUInt16();

                VectorTransform component = new VectorTransform(glyphIndex);

                if ((cFlags & 1) > 0)
                {
                    // The arguments are words
                    component.Dx = (float)parser.ReadInt16() / range;
                    component.Dy = (float)parser.ReadInt16() / range;
                }
                else
                {
                    // The arguments are bytes
                    component.Dx = (float)parser.ReadByte() / range;
                    component.Dy = (float)parser.ReadByte() / range;
                }

                if ((cFlags & 8) > 0)
                {
                    // We have one scale
                    component.XScale = component.YScale = parser.ReadF2Dot14();
                }
                else if ((cFlags & 64) > 0)
                {
                    // We have an X / Y scale
                    component.XScale = parser.ReadF2Dot14();
                    component.YScale = parser.ReadF2Dot14();
                }
                else if ((cFlags & 128) > 0)
                {
                    // We have a 2x2 transformation
                    component.XScale  = parser.ReadF2Dot14();
                    component.Scale01 = parser.ReadF2Dot14();
                    component.Scale10 = parser.ReadF2Dot14();
                    component.YScale  = parser.ReadF2Dot14();
                }

                // Push the component to the end:
                compGlyph.AddComponent(component);

                moreComponents = ((cFlags & 32) == 32);
            }

            return(compGlyph);
        }