예제 #1
0
        // Adds the SVG defined by svgContent to the font file at the specified glyphID
        public void AssembleSvgContent(ref TableRecord record, ref byte[] source, byte[] svgContent, ushort glyphID, FontModel fontModel)
        {
            start   = record.offset; // start always refer to the offset to the file head.
            current = record.offset;

            // Read SVG Main Header
            ushort version           = ReadDataAndIncreaseIndex <ushort>(source, ref current, sizeof(ushort));
            uint   svgDocIndexOffset = ReadDataAndIncreaseIndex <uint>(source, ref current, sizeof(uint));
            uint   reserved          = ReadDataAndIncreaseIndex <uint>(source, ref current, sizeof(uint));

            // Read document Index
            uint   documentIndexOffset       = start + svgDocIndexOffset;
            uint   svgDocIndexAbsoluteOffset = start + svgDocIndexOffset;
            ushort numEntries = ReadDataAndIncreaseIndex <ushort>(source, ref documentIndexOffset, sizeof(ushort));

            int  svgLength;
            int  paddingDiff = 0;
            long diff;

            byte[] recordLength;
            byte[] newSource;
            byte[] checksum;
            uint   newDocumentIndexOffset = 0;
            uint   newPadding             = 0;
            uint   currPadding            = CalculatePadding(record.length);
            bool   firstPass = true;

            SVGDocIdxEntry[] docIdxEntries = new SVGDocIdxEntry[numEntries];
            for (int i = 0; i < docIdxEntries.Length; i++)
            {
                docIdxEntries[i] = ReadSvgDocIdxEntry(source, ref documentIndexOffset);
                // Case 0: takes into account the glyphID already having SVG content
                if (docIdxEntries[i].startID.Equals(glyphID))
                {
                    svgLength = svgContent.Length;
                    diff      = docIdxEntries[i].docLength - svgLength;
                    uint svgOffset = docIdxEntries[i].docOffset + record.offset + svgDocIndexOffset;
                    recordLength = GetBytesBigEndian((uint)(record.length - diff));
                    // Changes the SVG table record length by the difference between the length of the old SVG content and the new SVG content
                    Array.Copy(recordLength, 0, source, (int)record.offsetOfOffset + sizeof(uint), sizeof(uint));
                    newPadding  = CalculatePadding((uint)(record.length - diff));
                    paddingDiff = (int)currPadding - (int)newPadding;
                    byte[] length = GetBytesBigEndian((uint)svgLength);
                    // Changes the length of the SVG content contained in the SVG Document Index Entry to reflect the length of the new SVG content
                    Array.Copy(length, 0, source, (int)(documentIndexOffset - sizeof(uint)), sizeof(uint));
                    // Change the table record offsets for tables following the altered SVG content to reflect the change in length
                    ChangeTableRecOffsets(fontModel.TableRecords, ref source, (diff + paddingDiff), start + svgDocIndexOffset);
                    // Changes the SVG offsets in the SVG Document Index Entries for the SVG content that was written after the altered SVG content in the
                    // font file to reflect a shift of the difference in length between the old and new SVG content
                    ChangeSvgOffsets(fontModel.TableRecords, ref source, diff, docIdxEntries[i].docOffset);
                    newSource = new byte[source.Length - (diff + paddingDiff)];
                    // Copies from the beginning of source to the beginning of the altered SVG content
                    Array.Copy(source, 0, newSource, 0, (int)svgOffset);
                    // Skips the length of the new SVG content in the newSource array and copies from the beginning of the SVG content following the altered SVG content
                    // to the end of the SVG Table
                    Array.Copy(source, (int)(svgOffset + docIdxEntries[i].docLength), newSource, (int)svgOffset + svgLength, (int)(record.offset + record.length - svgOffset - docIdxEntries[i].docLength));
                    // Makes sure the padding at the end of the SVG table in the newSource array is correct and copies from the beginning o
                    Array.Copy(source, (int)(record.offset + record.length + currPadding), newSource, (int)(svgLength + record.offset + record.length - docIdxEntries[i].docLength + newPadding), (int)(source.Length - (record.offset + record.length + currPadding)));
                    // Copies from the beginning of the table following the SVG Table (after the entire SVG table and it's padding) to the end of the font file
                    Array.Copy(svgContent, 0, newSource, (int)svgOffset, svgLength);
                    source        = newSource;
                    record.length = (uint)(record.length - diff);
                    checksum      = GetBytesBigEndian(CalcTableChecksum(record.offset, record.length, ref source));
                    Array.Copy(checksum, 0, source, (int)(record.offsetOfOffset - sizeof(uint)), sizeof(uint));
                    record.checksum = BitConverter.ToUInt32(checksum, 0);
                    return;
                }
                // Case 1: takes into account the glyphID not having SVG content and not being the last glyphID
                else if (docIdxEntries[i].startID > glyphID && firstPass)
                {
                    // newDocumentIndexOffset stores the location that the new SVG Document Index Entry would have to be added
                    newDocumentIndexOffset = documentIndexOffset - (uint)Marshal.SizeOf <SVGDocIdxEntry>();
                    firstPass = false;
                    break;
                }
            }
            // Case 2: takes into account the glyphID not having SVG content and being the last glyphID
            if (firstPass)
            {
                // newDocumentIndexOffset stores the location that the new SVG Document Index Entry would have to be added
                // In this case right after the last SVG Document Index Entry
                newDocumentIndexOffset = documentIndexOffset;
            }
            svgLength = svgContent.Length;
            // In cases 1 and 2 an entirely new SVG Document Index Entry must be added, so diff is the length of the SVG content plus the length of an
            // SVG Document Index Entry
            // This is made negative because for consistency's sake diff is always subtracted
            diff         = (svgLength + Marshal.SizeOf <SVGDocIdxEntry>()) * -1;
            recordLength = GetBytesBigEndian((uint)(record.length - diff));
            // Changes the SVG table record length by diff
            Array.Copy(recordLength, 0, source, (int)record.offsetOfOffset + sizeof(uint), sizeof(uint));
            newPadding  = CalculatePadding((uint)(record.length - diff));
            paddingDiff = (int)currPadding - (int)newPadding;
            // Change the table record offsets for tables following the added SVG content to reflect the change in length
            ChangeTableRecOffsets(fontModel.TableRecords, ref source, (diff + paddingDiff), start + svgDocIndexOffset);
            // Brand new SVG content is being added to the very end of the SVG table, so the offsets contained in the SVG
            // Document Index Entries need only be changed by the length of the new SVG Document Index Entry that will be addded
            ChangeSvgOffsets(fontModel.TableRecords, ref source, diff + svgLength, 0);
            uint numEntriesOffset = start + svgDocIndexOffset;

            byte[] newNumEntries = GetBytesBigEndian((ushort)(numEntries + 1));
            // Increments the number of entries contained in the SVG document index table
            Array.Copy(newNumEntries, 0, source, (int)numEntriesOffset, sizeof(ushort));
            newSource = new byte[source.Length - (diff + paddingDiff)];
            // Copies from the begining of source to the end of the SVG Document Index Entry preceeding the one to be added
            Array.Copy(source, 0, newSource, 0, (int)newDocumentIndexOffset);
            // Assumes the start and end glyphIDs are the same because currently you can only add one SVG at a time
            byte[] startAndEndGlyphID  = GetBytesBigEndian(glyphID);
            int    newSourceCopyOffset = (int)newDocumentIndexOffset;

            // Copies the startGlyphID to the newSource array
            Array.Copy(startAndEndGlyphID, 0, newSource, newSourceCopyOffset, sizeof(ushort));
            newSourceCopyOffset += sizeof(ushort);
            // Copies the endGlyphID to the newSource array
            Array.Copy(startAndEndGlyphID, 0, newSource, newSourceCopyOffset, sizeof(ushort));
            newSourceCopyOffset += sizeof(ushort);
            byte[] svgDocOffset = GetBytesBigEndian((uint)(record.length - svgDocIndexOffset + Marshal.SizeOf <SVGDocIdxEntry>()));
            // Copies the svgDocOffset to the newSource array
            Array.Copy(svgDocOffset, 0, newSource, newSourceCopyOffset, sizeof(uint));
            newSourceCopyOffset += sizeof(uint);
            byte[] svgDocLength = GetBytesBigEndian((uint)svgLength);
            // Copies the svgDocLength to the newSource array
            Array.Copy(svgDocLength, 0, newSource, newSourceCopyOffset, sizeof(uint));
            newSourceCopyOffset += sizeof(uint);
            // Copies from the begining of the SVG Document Index Entry following the one added to the end of the SVG Table in source
            Array.Copy(source, (int)newDocumentIndexOffset, newSource, newSourceCopyOffset, (int)(record.length - svgDocIndexOffset + svgDocIndexAbsoluteOffset - newDocumentIndexOffset));
            newSourceCopyOffset += (int)(record.length - svgDocIndexOffset + svgDocIndexAbsoluteOffset - newDocumentIndexOffset);
            // Copies the new SVG content to newSource
            Array.Copy(svgContent, 0, newSource, newSourceCopyOffset, svgLength);
            // Makes sure the padding on the SVG Table remains correct
            newSourceCopyOffset += (int)(svgLength + newPadding);
            // Copies from the beginning of the table following the SVG table to the end of the file
            Array.Copy(source, (int)svgDocIndexAbsoluteOffset + (int)(record.length - svgDocIndexOffset + currPadding), newSource, newSourceCopyOffset, source.Length - ((int)svgDocIndexAbsoluteOffset + (int)(record.length - svgDocIndexOffset + currPadding)));
            source        = newSource;
            record.length = (uint)(record.length - diff);
            checksum      = GetBytesBigEndian(CalcTableChecksum(record.offset, record.length, ref source));
            Array.Copy(checksum, 0, source, (int)(record.offsetOfOffset - sizeof(uint)), sizeof(uint));
            record.checksum = BitConverter.ToUInt32(checksum, 0);
        }
예제 #2
0
        // Removes SVG of the specified glyph ID from the SVG table of the font.
        public void RemoveSvgContent(ref TableRecord record, ref byte[] source, ushort glyphID, FontModel fontModel)
        {
            start   = record.offset; // Start always refers to the offset to the file head.
            current = record.offset;

            // Read SVG main header.
            ushort version           = ReadDataAndIncreaseIndex <ushort>(source, ref current, sizeof(ushort));
            uint   svgDocIndexOffset = ReadDataAndIncreaseIndex <uint>(source, ref current, sizeof(uint));
            uint   reserved          = ReadDataAndIncreaseIndex <uint>(source, ref current, sizeof(uint));

            // Read document index.
            uint   documentIndexOffset       = start + svgDocIndexOffset;
            uint   svgDocIndexAbsoluteOffset = start + svgDocIndexOffset;
            ushort numEntries = ReadDataAndIncreaseIndex <ushort>(source, ref documentIndexOffset, sizeof(ushort));

            uint currPadding = CalculatePadding(record.length);

            SVGDocIdxEntry[] docIdxEntries = new SVGDocIdxEntry[numEntries];
            for (int i = 0; i < docIdxEntries.Length; i++)
            {
                docIdxEntries[i] = ReadSvgDocIdxEntry(source, ref documentIndexOffset);
                // Checks that SVG Document Index Entry matches the one to remove.
                // Note: Currently only supports one-to-one SVG/glyph ID mapping.
                if (docIdxEntries[i].startID.Equals(glyphID))
                {
                    int svgLength = (int)docIdxEntries[i].docLength;

                    // diff calculates the change in length of the font file because of removing the SVG.
                    // diff = length of the SVG content to remove + length of the SVG document index entry
                    long diff = svgLength + Marshal.SizeOf <SVGDocIdxEntry>();

                    uint svgOffset = docIdxEntries[i].docOffset + record.offset + svgDocIndexOffset; // Offset to the beginning of the SVG content

                    // Edits the SVG table length stored in the table record for SVG to the current length - diff
                    byte[] recordLength = GetBytesBigEndian((uint)(record.length - diff));
                    Array.Copy(recordLength, 0, source, (int)record.offsetOfOffset + sizeof(uint), sizeof(uint));

                    uint newPadding  = CalculatePadding((uint)(record.length - diff));
                    int  paddingDiff = (int)currPadding - (int)newPadding;
                    // Change the table record offsets for tables following the removed SVG content
                    ChangeTableRecOffsets(fontModel.TableRecords, ref source, (diff + paddingDiff), start + svgDocIndexOffset);
                    // Changes all of the SVG offsets in the SVG Document Index Entries to reflect an upward shift of 12 as caused by the removal of
                    // the specified SVG Document Index Entry
                    ChangeSvgOffsets(fontModel.TableRecords, ref source, diff - svgLength, 0);
                    // Changes the SVG offsets in the SVG Document Index Entries for the SVG content that was written after the removed SVG content in the
                    // font file to reflect an upward shift of the length of the removed SVG content
                    ChangeSvgOffsets(fontModel.TableRecords, ref source, diff - Marshal.SizeOf <SVGDocIdxEntry>(), docIdxEntries[i].docOffset);

                    uint numEntriesOffset = start + svgDocIndexOffset;

                    // Decrements the number specifying the number of SVG entries and edits font appropriately
                    byte[] newNumEntries = GetBytesBigEndian((ushort)(numEntries - 1));
                    Array.Copy(newNumEntries, 0, source, (int)numEntriesOffset, sizeof(ushort));

                    // Creates byte array for the corrected font and copies over font while removing SVG content and
                    // changing the padding of the SVG table appropriately

                    byte[] newSource = new byte[source.Length - (diff + paddingDiff)];

                    // Copies from the beginning of source to the end of the SVG Document Index Entry preceeding the one to be removed
                    int sourceIndex    = 0;
                    int newSourceIndex = 0;
                    int length         = (int)(documentIndexOffset - Marshal.SizeOf <SVGDocIdxEntry>());
                    Array.Copy(source, sourceIndex, newSource, newSourceIndex, length);

                    // Skips the DocIdxEntry to remove and copies from the beginning of the SVG DocIdxEntry following the removed one to the end of the SVG content preceeding the removed SVG content
                    sourceIndex    += (int)(documentIndexOffset);
                    newSourceIndex += length;
                    length          = (int)(svgDocIndexAbsoluteOffset + docIdxEntries[i].docOffset - documentIndexOffset);
                    Array.Copy(source, sourceIndex, newSource, newSourceIndex, length);

                    // Copies from the beginning of the SVG content following the SVG content to remove until the end of the SVG table
                    // Stops copying at this point in order to make sure the padding at the end of the SVG table is correct
                    sourceIndex    += length + (int)docIdxEntries[i].docLength;
                    newSourceIndex += length;
                    length          = (int)(record.offset + record.length - (svgDocIndexAbsoluteOffset + docIdxEntries[i].docOffset + docIdxEntries[i].docLength));
                    Array.Copy(source, sourceIndex, newSource, newSourceIndex, length);

                    // Copies from the beginning of the table following the SVG Table (after the entire SVG table and its padding) to the end of the font file
                    sourceIndex    = (int)(record.offset + record.length + currPadding);
                    newSourceIndex = (int)(record.offset + record.length - docIdxEntries[i].docLength - Marshal.SizeOf <SVGDocIdxEntry>() + newPadding);
                    length         = (int)(source.Length - (record.offset + record.length + currPadding));
                    Array.Copy(source, sourceIndex, newSource, newSourceIndex, length);

                    // Set the new source.
                    source = newSource;

                    // Alters the TableRecord object to reflect the new length of the table
                    record.length = (uint)(record.length - diff);

                    // Edits checksum of SVG table
                    byte[] checksum = GetBytesBigEndian(CalcTableChecksum(record.offset, record.length, ref source));
                    Array.Copy(checksum, 0, source, (int)(record.offsetOfOffset - sizeof(uint)), sizeof(uint));
                    record.checksum = BitConverter.ToUInt32(checksum, 0);
                    return;
                }
            }
        }