diff options
Diffstat (limited to 'gfx/sfntly/cpp/src/test')
45 files changed, 10174 insertions, 0 deletions
diff --git a/gfx/sfntly/cpp/src/test/autogenerated/cmap_basic_test.cc b/gfx/sfntly/cpp/src/test/autogenerated/cmap_basic_test.cc new file mode 100644 index 0000000000..083b27fa9d --- /dev/null +++ b/gfx/sfntly/cpp/src/test/autogenerated/cmap_basic_test.cc @@ -0,0 +1,131 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// type.h needs to be included first because of building issues on Windows +// Type aliases we delcare are defined in other headers and make the build +// fail otherwise. +#include "sfntly/port/type.h" +#include <assert.h> +#include <stdio.h> +#include <unicode/ucnv.h> + +#include <iostream> +#include <string> + +#include "gtest/gtest.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/table/core/font_header_table.h" +#include "sfntly/tag.h" +#include "test/autogenerated/cmap_test_data.h" +#include "test/test_font_utils.h" +#include "test/test_utils.h" +#include "test/test_xml_utils.h" + +namespace sfntly { + +#if GTEST_HAS_PARAM_TEST + +using ::testing::TestWithParam; +using ::testing::Values; + +class CMapBasicTests : public :: testing::TestWithParam<const char*> { + public: + CMapBasicTests() {} + virtual void SetUp(); + virtual void TearDown() {} + + Ptr<CMapTable> cmap_table_; + TiXmlDocument document_; +}; + +void CMapBasicTests::SetUp() { + // Loading the font + Ptr<FontFactory> font_factory; + font_factory.Attach(FontFactory::GetInstance()); + FontArray font_array; + std::string font_name = "../../"; +#if defined (WIN32) + font_name += "../"; +#endif + font_name += std::string(GetParam()); + LoadFont(font_name.c_str(), font_factory, &font_array); + ASSERT_FALSE(font_array.empty()); + Ptr<Font> font = font_array.at(0); + ASSERT_NE(font, static_cast<Font*>(NULL)); + cmap_table_ = down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + if (!cmap_table_) + fprintf(stderr, "No CMap: %s\n", font_name.c_str()); + ASSERT_NE(cmap_table_, static_cast<CMapTable*>(NULL)); + + // Loading the XML file + document_ = TiXmlDocument((font_name + ".xml").c_str()); + ASSERT_TRUE(document_.LoadFile()); +} + +TEST_P(CMapBasicTests, BasicTest) { + TiXmlNodeVector* cmap_table = GetNodesWithName(&document_, "cmap_table"); + // A font can only have one CMap table + ASSERT_EQ(cmap_table->size(), (size_t)1); + TiXmlNodeVector* cmaps = GetNodesWithName(cmap_table->at(0), "cmap"); + const TiXmlAttribute* num_cmaps_attr = GetAttribute(cmap_table->at(0), + "num_cmaps"); + ASSERT_NE(num_cmaps_attr, static_cast<TiXmlAttribute*>(NULL)); + // But there may be more than one CMap in this table + ASSERT_LE(cmaps->size(), (size_t)num_cmaps_attr->IntValue()); + for (TiXmlNodeVector::iterator it = cmaps->begin(); + it != cmaps->end(); ++it) { + int32_t platform_id = GetAttribute(*it, "platform_id")->IntValue(); + int32_t encoding_id = GetAttribute(*it, "encoding_id")->IntValue(); + Ptr<CMapTable::CMap> cmap; + cmap.Attach(cmap_table_->GetCMap(platform_id, encoding_id)); + if (!cmap) { + fprintf(stderr, "Cannot test unsupported CMapFormat%d\n", + GetAttribute(*it, "format")->IntValue()); + continue; + } + ASSERT_EQ(cmap->platform_id(), platform_id); + ASSERT_EQ(cmap->encoding_id(), encoding_id); + TiXmlNodeVector* maps = GetNodesWithName(*it, "map"); + for (TiXmlNodeVector::iterator jt = maps->begin(); + jt != maps->end(); ++jt) { + int32_t character; +#if defined (WIN32) + sscanf_s(GetAttribute(*jt, "char")->Value(), "%x", &character); +#else + sscanf(GetAttribute(*jt, "char")->Value(), "%x", &character); +#endif + int32_t glyph_id = GetAttribute(*jt, "gid")->IntValue(); + ASSERT_EQ(cmap->GlyphId(character), glyph_id); + } + delete maps; + } + delete cmaps; + delete cmap_table; +} + +INSTANTIATE_TEST_CASE_P(CMapBasicTests, + CMapBasicTests, + ::testing::ValuesIn(cmap_test_data::kAllTests)); + +#else + +TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {} + +#endif // GTEST_HAS_PARAM +} diff --git a/gfx/sfntly/cpp/src/test/autogenerated/cmap_test_data.h b/gfx/sfntly/cpp/src/test/autogenerated/cmap_test_data.h new file mode 100644 index 0000000000..4a9f267d1c --- /dev/null +++ b/gfx/sfntly/cpp/src/test/autogenerated/cmap_test_data.h @@ -0,0 +1,185 @@ +/* + * !!! DO NOT EDIT !!! + * THIS FILE IS GENERATED BY A SCRIPT. + * FOR MORE DETAILS SEE 'README-test_data.txt'. + */ + +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_ + +#include "sfntly/port/type.h" + +namespace sfntly { +namespace cmap_test_data { +const char* kAllTests[] = { + "data/fonts/cousine/Cousine-BoldItalic.ttf", + "data/fonts/cousine/Cousine-Bold.ttf", + "data/fonts/cousine/Cousine-Regular.ttf", + "data/fonts/cousine/Cousine-Italic.ttf", + "data/fonts/cedarvillecursive/Cedarville-Cursive.ttf", + "data/fonts/dancingscript/DancingScript-Bold.ttf", + "data/fonts/dancingscript/DancingScript-Regular.ttf", + "data/fonts/damion/Damion-Regular.ttf", + "data/fonts/annieuseyourtelescope/AnnieUseYourTelescope.ttf", + "data/fonts/deliusswashcaps/DeliusSwashCaps-Regular.ttf", + "data/fonts/delius/Delius-Regular.ttf", + "data/fonts/coveredbyyourgrace/CoveredByYourGrace.ttf", + "data/fonts/ebgaramond/EBGaramond-Regular.ttf", + "data/fonts/cabinsketch/CabinSketch-Bold.ttf", + "data/fonts/allan/Allan-Bold.ttf", + "data/fonts/coda/Coda-Caption-Heavy.ttf", + "data/fonts/coda/Coda-Heavy.ttf", + "data/fonts/forum/Forum-Regular.ttf", + "data/fonts/alike/Alike-Regular.ttf", + "data/fonts/corben/Corben-Bold.ttf", + "data/fonts/caudex/Caudex-Regular.ttf", + "data/fonts/caudex/Caudex-BoldItalic.ttf", + "data/fonts/caudex/Caudex-Italic.ttf", + "data/fonts/caudex/Caudex-Bold.ttf", + "data/fonts/cabin/Cabin-MediumItalic.ttf", + "data/fonts/cabin/Cabin-SemiBold.ttf", + "data/fonts/cabin/Cabin-SemiBoldItalic.ttf", + "data/fonts/cabin/Cabin-BoldItalic.ttf", + "data/fonts/cabin/Cabin-Medium.ttf", + "data/fonts/cabin/Cabin-Italic.ttf", + "data/fonts/cabin/Cabin-Bold.ttf", + "data/fonts/cabin/Cabin-Regular.ttf", + "data/fonts/dawningofanewday/DawningofaNewDay.ttf", + "data/fonts/dangrek/Dangrek.ttf", + "data/fonts/blackopsone/BlackOpsOne.ttf", + "data/fonts/francoisone/FrancoisOne.ttf", + "data/fonts/bowlbyone/BowlbyOne.ttf", + "data/fonts/bowlbyone/BowlbyOneSC.ttf", + "data/fonts/calligraffiti/Calligraffiti.ttf", + "data/fonts/bangers/Bangers.ttf", + "data/fonts/astloch/Astloch-Regular.ttf", + "data/fonts/astloch/Astloch-Bold.ttf", + "data/fonts/angkor/Angkor.ttf", + "data/fonts/abrilfatface/AbrilFatface-Regular.ttf", + "data/fonts/cantarell/Cantarell-BoldOblique.ttf", + "data/fonts/cantarell/Cantarell-Oblique.ttf", + "data/fonts/cantarell/Cantarell-Bold.ttf", + "data/fonts/cantarell/Cantarell-Regular.ttf", + "data/fonts/arvo/Arvo-Bold.ttf", + "data/fonts/arvo/Arvo-Italic.ttf", + "data/fonts/arvo/Arvo-BoldItalic.ttf", + "data/fonts/arvo/Arvo-Regular.ttf", + "data/fonts/chewy/Chewy.ttf", + "data/fonts/bigshotone/BigshotOne.ttf", + "data/fonts/chenla/Chenla.ttf", + "data/fonts/bayon/Bayon.ttf", + "data/fonts/coustard/Coustard-Black.ttf", + "data/fonts/coustard/Coustard-Regular.ttf", + "data/fonts/amaticsc/AmaticSC-Bold.ttf", + "data/fonts/amaticsc/AmaticSC-Regular.ttf", + "data/fonts/comfortaa/Comfortaa-Light.ttf", + "data/fonts/comfortaa/Comfortaa-Bold.ttf", + "data/fonts/comfortaa/Comfortaa-Regular.ttf", + "data/fonts/expletussans/ExpletusSans-Bold.ttf", + "data/fonts/expletussans/ExpletusSans-MediumItalic.ttf", + "data/fonts/expletussans/ExpletusSans-Medium.ttf", + "data/fonts/expletussans/ExpletusSans-SemiBold.ttf", + "data/fonts/expletussans/ExpletusSans-Regular.ttf", + "data/fonts/expletussans/ExpletusSans-Italic.ttf", + "data/fonts/expletussans/ExpletusSans-SemiBoldItalic.ttf", + "data/fonts/expletussans/ExpletusSans-BoldItalic.ttf", + "data/fonts/aubrey/Aubrey-Regular.ttf", + "data/fonts/antic/Antic-Regular.ttf", + "data/fonts/copse/Copse-Regular.ttf", + "data/fonts/daysone/DaysOne-Regular.ttf", + "data/fonts/actor/Actor-Regular.ttf", + "data/fonts/bentham/Bentham-Regular.ttf", + "data/fonts/federo/Federo-Regular.ttf", + "data/fonts/arimo/Arimo-Italic.ttf", + "data/fonts/arimo/Arimo-BoldItalic.ttf", + "data/fonts/arimo/Arimo-Bold.ttf", + "data/fonts/arimo/Arimo-Regular.ttf", + "data/fonts/felltypes/IMFeDPsc28P.ttf", + "data/fonts/felltypes/IMFeGPrm28P.ttf", + "data/fonts/felltypes/IMFeENsc28P.ttf", + "data/fonts/felltypes/IMFePIit28P.ttf", + "data/fonts/felltypes/IMFeDPrm28P.ttf", + "data/fonts/felltypes/IMFePIrm28P.ttf", + "data/fonts/felltypes/IMFeFCrm28P.ttf", + "data/fonts/felltypes/IMFeGPit28P.ttf", + "data/fonts/felltypes/IMFeENit28P.ttf", + "data/fonts/felltypes/IMFeDPit28P.ttf", + "data/fonts/felltypes/IMFeENrm28P.ttf", + "data/fonts/felltypes/IMFeGPsc28P.ttf", + "data/fonts/felltypes/IMFePIsc28P.ttf", + "data/fonts/felltypes/IMFeFCsc28P.ttf", + "data/fonts/felltypes/IMFeFCit28P.ttf", + "data/fonts/bokor/Bokor.ttf", + "data/fonts/didactgothic/DidactGothic.ttf", + "data/fonts/allerta/Allerta-Medium.ttf", + "data/fonts/allerta/Allerta-Stencil.ttf", + "data/fonts/buda/Buda-Light.ttf", + "data/fonts/brawler/Brawler-Regular.ttf", + "data/fonts/carterone/CarterOne.ttf", + "data/fonts/candal/Candal.ttf", + "data/fonts/dorsa/Dorsa-Regular.ttf", + "data/fonts/crimson/CrimsonText-BoldItalic.ttf", + "data/fonts/crimson/CrimsonText-Bold.ttf", + "data/fonts/crimson/CrimsonText-Italic.ttf", + "data/fonts/crimson/CrimsonText-Roman.ttf", + "data/fonts/crimson/CrimsonText-Semibold.ttf", + "data/fonts/crimson/CrimsonText-SemiboldItalic.ttf", + "data/fonts/amaranth/Amaranth-Italic.ttf", + "data/fonts/amaranth/Amaranth-Bold.ttf", + "data/fonts/amaranth/Amaranth-Regular.ttf", + "data/fonts/amaranth/Amaranth-BoldItalic.ttf", + "data/fonts/crushed/Crushed.ttf", + "data/fonts/adamina/Adamina-Regular.ttf", + "data/fonts/aldrich/Aldrich-Regular.ttf", + "data/fonts/fanwoodtext/FanwoodText-Italic.ttf", + "data/fonts/fanwoodtext/FanwoodText-Regular.ttf", + "data/fonts/anonymouspro/AnonymousPro-Regular.ttf", + "data/fonts/anonymouspro/AnonymousPro-Bold.ttf", + "data/fonts/anonymouspro/AnonymousPro-Italic.ttf", + "data/fonts/anonymouspro/AnonymousPro-BoldItalic.ttf", + "data/fonts/bevan/Bevan.ttf", + "data/fonts/artifika/Artifika-Regular.ttf", + "data/fonts/anton/Anton.ttf", + "data/fonts/battambang/Battambang-Regular.ttf", + "data/fonts/battambang/Battambang-Bold.ttf", + "data/fonts/carme/Carme-Regular.ttf", + "data/fonts/cherrycreamsoda/CherryCreamSoda.ttf", + "data/fonts/deliusunicase/DeliusUnicase-Regular.ttf", + "data/fonts/comingsoon/ComingSoon.ttf", + "data/fonts/freehand/Freehand.ttf", + "data/fonts/alice/Alice-Regular.ttf", + "data/fonts/contrail/Contrail-Regular.ttf", + "data/fonts/cardo/Cardo-Bold.ttf", + "data/fonts/cardo/Cardo-Italic.ttf", + "data/fonts/cardo/Cardo-Regular.ttf", + "data/fonts/asset/Asset.ttf", + "data/fonts/changaone/ChangaOne-Regular.ttf", + "data/fonts/aclonica/Aclonica.ttf", + "data/fonts/craftygirls/CraftyGirls.ttf", + "data/fonts/architectsdaughter/ArchitectsDaughter.ttf", + "data/fonts/content/Content-Bold.ttf", + "data/fonts/content/Content-Regular.ttf", + "data/fonts/abel/Abel-Regular.ttf", + "data/fonts/cuprum/Cuprum.ttf", + "data/fonts/andika/Andika-R.ttf" +}; +} // namespace cmap_test_data +} // namespace sfntly + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_ diff --git a/gfx/sfntly/cpp/src/test/bitmap_table_test.cc b/gfx/sfntly/cpp/src/test/bitmap_table_test.cc new file mode 100644 index 0000000000..e9eacc7aac --- /dev/null +++ b/gfx/sfntly/cpp/src/test/bitmap_table_test.cc @@ -0,0 +1,216 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/port/file_input_stream.h" +#include "sfntly/port/memory_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "sfntly/table/bitmap/ebdt_table.h" +#include "sfntly/table/bitmap/eblc_table.h" +#include "sfntly/table/bitmap/index_sub_table_format3.h" +#include "sfntly/table/bitmap/index_sub_table_format4.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +const int32_t NUM_STRIKES = 4; +const int32_t STRIKE1_ARRAY_OFFSET = 0xc8; +const int32_t STRIKE1_INDEX_TABLE_SIZE = 0x4f4; +const int32_t STRIKE1_NUM_INDEX_TABLES = 1; +const int32_t STRIKE1_COLOR_REF = 0; +const int32_t STRIKE1_START_GLYPH_INDEX = 0; +const int32_t STRIKE1_END_GLYPH_INDEX = 623; +const int32_t STRIKE1_PPEM_X = 10; +const int32_t STRIKE1_PPEM_Y = 10; +const int32_t STRIKE1_BIT_DEPTH = 1; +const int32_t STRIKE1_FLAGS = 0x01; + +const int32_t STRIKE4_SUB1_INDEX_FORMAT = 3; +const int32_t STRIKE4_SUB1_IMAGE_FORMAT = 1; +const int32_t STRIKE4_SUB1_IMAGE_DATA_OFFSET = 0x00005893; +const int32_t STRIKE4_SUB1_GLYPH_OFFSET[] = { + 0x00005893, 0x00005898, 0x0000589d, 0x000058a2, 0x000058a7, + 0x000058b2, 0x000058c2, 0x000058d0, 0x000058de, 0x000058e6 }; +const int32_t NUM_STRIKE4_SUB1_GLYPH_OFFSET = 10; +const int32_t STRIKE4_SUB1_GLYPH2_LENGTH = 0x58a2 - 0x589d; + +bool CommonReadingTest(Font* raw_font) { + FontPtr font = raw_font; + + EblcTablePtr bitmap_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); + EbdtTablePtr bitmap_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT)); + + EXPECT_FALSE(bitmap_loca == NULL); + EXPECT_FALSE(bitmap_table == NULL); + + if (!bitmap_loca) { + return false; + } + + EXPECT_EQ(bitmap_loca->NumSizes(), NUM_STRIKES); + + // Strike 1 + BitmapSizeTablePtr strike1 = bitmap_loca->GetBitmapSizeTable(0); + EXPECT_FALSE(strike1 == NULL); + if (!strike1) { + return false; + } + EXPECT_EQ(strike1->IndexSubTableArrayOffset(), STRIKE1_ARRAY_OFFSET); + EXPECT_EQ(strike1->NumberOfIndexSubTables(), STRIKE1_NUM_INDEX_TABLES); + EXPECT_EQ(strike1->ColorRef(), STRIKE1_COLOR_REF); + EXPECT_EQ(strike1->StartGlyphIndex(), STRIKE1_START_GLYPH_INDEX); + EXPECT_EQ(strike1->EndGlyphIndex(), STRIKE1_END_GLYPH_INDEX); + EXPECT_EQ(strike1->PpemX(), STRIKE1_PPEM_X); + EXPECT_EQ(strike1->PpemY(), STRIKE1_PPEM_Y); + EXPECT_EQ(strike1->BitDepth(), STRIKE1_BIT_DEPTH); + EXPECT_EQ(strike1->FlagsAsInt(), STRIKE1_FLAGS); + + // Strike 4 + // In this test font, all strikes and all subtables have same glyphs. + BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3); + EXPECT_FALSE(strike4 == NULL); + EXPECT_EQ(strike4->StartGlyphIndex(), STRIKE1_START_GLYPH_INDEX); + EXPECT_EQ(strike4->EndGlyphIndex(), STRIKE1_END_GLYPH_INDEX); + IndexSubTablePtr sub1 = strike4->GetIndexSubTable(0); + EXPECT_FALSE(sub1 == NULL); + EXPECT_EQ(sub1->image_format(), STRIKE4_SUB1_IMAGE_FORMAT); + EXPECT_EQ(sub1->first_glyph_index(), STRIKE1_START_GLYPH_INDEX); + EXPECT_EQ(sub1->last_glyph_index(), STRIKE1_END_GLYPH_INDEX); + EXPECT_EQ(sub1->image_data_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET); + + for (int32_t i = 0; i < NUM_STRIKE4_SUB1_GLYPH_OFFSET; ++i) { + EXPECT_EQ(sub1->GlyphOffset(i), STRIKE4_SUB1_GLYPH_OFFSET[i]); + } + return true; +} + +bool TestReadingBitmapTable() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontArray font_array; + LoadFont(SAMPLE_BITMAP_FONT, factory, &font_array); + FontPtr font = font_array[0]; + EXPECT_TRUE(CommonReadingTest(font)); + + EblcTablePtr bitmap_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); + BitmapSizeTablePtr strike1 = bitmap_loca->GetBitmapSizeTable(0); + BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3); + IndexSubTablePtr sub1 = strike4->GetIndexSubTable(0); + + EXPECT_EQ(strike1->IndexTableSize(), STRIKE1_INDEX_TABLE_SIZE); + EXPECT_EQ(sub1->index_format(), STRIKE4_SUB1_INDEX_FORMAT); + + // Strike 4 Index Sub Table 1 is a Format 3 + IndexSubTableFormat3Ptr sub3 = + down_cast<IndexSubTableFormat3*>(strike4->GetIndexSubTable(0)); + EXPECT_FALSE(sub3 == NULL); + BitmapGlyphInfoPtr info; + info.Attach(sub3->GlyphInfo(2)); + EXPECT_EQ(info->glyph_id(), 2); + EXPECT_EQ(info->block_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET); + EXPECT_EQ(info->start_offset(), + STRIKE4_SUB1_GLYPH_OFFSET[2] - STRIKE4_SUB1_GLYPH_OFFSET[0]); + EXPECT_EQ(info->format(), STRIKE4_SUB1_IMAGE_FORMAT); + EXPECT_EQ(info->length(), STRIKE4_SUB1_GLYPH2_LENGTH); + + return true; +} + +// Function in subset_impl.cc +extern +void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca); + +bool TestIndexFormatConversion() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray builder_array; + BuilderForFontFile(SAMPLE_BITMAP_FONT, factory, &builder_array); + + FontBuilderPtr font_builder; + font_builder = builder_array[0]; + EblcTableBuilderPtr eblc_builder = + down_cast<EblcTable::Builder*>(font_builder->GetTableBuilder(Tag::EBLC)); + BitmapLocaList new_loca; + eblc_builder->GenerateLocaList(&new_loca); + SubsetEBLC(eblc_builder, new_loca); // Format 3 -> 4 + + FontPtr new_font; + new_font.Attach(font_builder->Build()); + + // Serialize and reload the serialized font. + MemoryOutputStream os; + factory->SerializeFont(new_font, &os); + +#if defined (SFNTLY_DEBUG_BITMAP) + SerializeToFile(&os, "anon-mod.ttf"); +#endif + + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + FontArray font_array; + factory->LoadFonts(&is, &font_array); + new_font = font_array[0]; + + EXPECT_TRUE(CommonReadingTest(new_font)); + + // Strike 4 Index Sub Table 1 is a Format 4 + EblcTablePtr bitmap_loca = + down_cast<EblcTable*>(new_font->GetTable(Tag::EBLC)); + BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3); + IndexSubTableFormat4Ptr sub4 = + down_cast<IndexSubTableFormat4*>(strike4->GetIndexSubTable(0)); + EXPECT_FALSE(sub4 == NULL); + + // And this subtable shall have exactly the same offset as original table + // since no subsetting happens. + FontArray original_font_array; + LoadFont(SAMPLE_BITMAP_FONT, factory, &original_font_array); + FontPtr font = original_font_array[0]; + EXPECT_FALSE(font == NULL); + EblcTablePtr original_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); + EXPECT_FALSE(original_loca == NULL); + BitmapSizeTablePtr original_strike4 = bitmap_loca->GetBitmapSizeTable(3); + EXPECT_FALSE(original_strike4 == NULL); + IndexSubTableFormat3Ptr sub3 = + down_cast<IndexSubTableFormat3*>(strike4->GetIndexSubTable(0)); + EXPECT_FALSE(sub3 == NULL); + EXPECT_EQ(strike4->StartGlyphIndex(), original_strike4->StartGlyphIndex()); + EXPECT_EQ(strike4->EndGlyphIndex(), original_strike4->EndGlyphIndex()); + for (int32_t i = strike4->StartGlyphIndex(); + i <= strike4->EndGlyphIndex(); ++i) { + BitmapGlyphInfoPtr info, original_info; + info.Attach(sub4->GlyphInfo(i)); + original_info.Attach(sub3->GlyphInfo(i)); + EXPECT_EQ(info->format(), original_info->format()); + EXPECT_EQ(info->glyph_id(), original_info->glyph_id()); + EXPECT_EQ(info->length(), original_info->length()); + EXPECT_EQ(info->offset(), original_info->offset()); + } + + return true; +} + +} // namespace sfntly + +TEST(BitmapTable, Reading) { + ASSERT_TRUE(sfntly::TestReadingBitmapTable()); +} + +TEST(BitmapTable, IndexFormatConversion) { + ASSERT_TRUE(sfntly::TestIndexFormatConversion()); +} diff --git a/gfx/sfntly/cpp/src/test/byte_array_test.cc b/gfx/sfntly/cpp/src/test/byte_array_test.cc new file mode 100644 index 0000000000..de50089ee2 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/byte_array_test.cc @@ -0,0 +1,146 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "gtest/gtest.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/data/growable_memory_byte_array.h" + +namespace sfntly { +namespace byte_array_test { + +const int32_t BYTE_ARRAY_SIZES[] = + {1, 7, 127, 128, 129, 255, 256, 257, 666, 1023, 10000, 0xffff, 0x10000}; + +void FillTestByteArray(ByteArray* ba, int32_t size) { + for (int32_t i = 0; i < size; ++i) { + ba->Put(i, (uint8_t)(i % 256)); + } +} + +void ReadByteArrayWithBuffer(ByteArray* ba, ByteVector* buffer, ByteVector* b) { + b->resize(ba->Length()); + int32_t index = 0; + while (index < ba->Length()) { + int32_t bytes_read = ba->Get(index, buffer); + std::copy(buffer->begin(), buffer->begin() + bytes_read, + b->begin() + index); + index += bytes_read; + } +} + +void ReadByteArrayWithSlidingWindow(ByteArray* ba, int window_size, + ByteVector* b) { + b->resize(ba->Length()); + int32_t index = 0; + int32_t actual_window_size = window_size; + while (index < ba->Length()) { + actual_window_size = + std::min<int32_t>(actual_window_size, b->size() - index); + int32_t bytes_read = ba->Get(index, &((*b)[0]), index, actual_window_size); + index += bytes_read; + } +} + +bool ReadComparison(ByteArray* ba1, ByteArray* ba2) { + // single byte reads + for (int i = 0; i < ba1->Length(); ++i) { + EXPECT_EQ(ba1->Get(i), ba2->Get(i)); + } + + ByteVector b1, b2; + // buffer reads + int increments = std::max<int32_t>(ba1->Length() / 11, 1); + for (int buffer_size = 1; buffer_size < ba1->Length(); + buffer_size += increments) { + ByteVector buffer(buffer_size); + ReadByteArrayWithBuffer(ba1, &buffer, &b1); + ReadByteArrayWithBuffer(ba2, &buffer, &b2); + EXPECT_GT(b1.size(), static_cast<size_t>(0)); + EXPECT_EQ(b1.size(), b2.size()); + EXPECT_TRUE(std::equal(b1.begin(), b1.end(), b2.begin())); + } + + // sliding window reads + b1.clear(); + b2.clear(); + for (int window_size = 1; window_size < ba1->Length(); + window_size += increments) { + ReadByteArrayWithSlidingWindow(ba1, window_size, &b1); + ReadByteArrayWithSlidingWindow(ba2, window_size, &b2); + EXPECT_GT(b1.size(), static_cast<size_t>(0)); + EXPECT_EQ(b1.size(), b2.size()); + EXPECT_TRUE(std::equal(b1.begin(), b1.end(), b2.begin())); + } + + return true; +} + +bool CopyTest(ByteArray* ba) { + ByteArrayPtr fixed_copy = new MemoryByteArray(ba->Length()); + ba->CopyTo(fixed_copy); + EXPECT_EQ(ba->Length(), fixed_copy->Length()); + EXPECT_TRUE(ReadComparison(ba, fixed_copy)); + + ByteArrayPtr growable_copy = new GrowableMemoryByteArray(); + ba->CopyTo(growable_copy); + EXPECT_EQ(ba->Length(), growable_copy->Length()); + EXPECT_TRUE(ReadComparison(ba, growable_copy)); + + return true; +} + +bool ByteArrayTester(ByteArray* ba) { + return CopyTest(ba); +} + +} // namespace byte_array_test + +bool TestMemoryByteArray() { + fprintf(stderr, "fixed mem: size "); + for (size_t i = 0; + i < sizeof(byte_array_test::BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { + int32_t size = byte_array_test::BYTE_ARRAY_SIZES[i]; + fprintf(stderr, "%d ", size); + ByteArrayPtr ba = new MemoryByteArray(size); + byte_array_test::FillTestByteArray(ba, size); + EXPECT_TRUE(byte_array_test::ByteArrayTester(ba)); + } + fprintf(stderr, "\n"); + return true; +} + +bool TestGrowableMemoryByteArray() { + fprintf(stderr, "growable mem: size "); + for (size_t i = 0; + i < sizeof(byte_array_test::BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { + int32_t size = byte_array_test::BYTE_ARRAY_SIZES[i]; + fprintf(stderr, "%d ", size); + ByteArrayPtr ba = new GrowableMemoryByteArray(); + byte_array_test::FillTestByteArray(ba, size); + EXPECT_TRUE(byte_array_test::ByteArrayTester(ba)); + } + fprintf(stderr, "\n"); + return true; +} + +} // namespace sfntly + +TEST(ByteArray, All) { + ASSERT_TRUE(sfntly::TestMemoryByteArray()); + ASSERT_TRUE(sfntly::TestGrowableMemoryByteArray()); +} diff --git a/gfx/sfntly/cpp/src/test/chrome_subsetter.cc b/gfx/sfntly/cpp/src/test/chrome_subsetter.cc new file mode 100644 index 0000000000..9563ab1576 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/chrome_subsetter.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sample/chromium/font_subsetter.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace { + // Use an additional variable to easily change name for testing. + const char* kInputFileName = sfntly::SAMPLE_TTF_FILE; + const char* kFontName = "Tuffy"; + const char* kOutputFileName = "tuffy-s.ttf"; + // The subset we want: Hello, world! + // The array is unsorted to verify that the subsetter gets the glyph id + // correctly. + const unsigned int kGlyphIds[] = { 43, 72, 79, 82, 15, 3, 90, 85, 71, 4 }; + const unsigned int kGlyphIdsCount = sizeof(kGlyphIds) / sizeof(unsigned int); +} + +// This function is deliberately located at global namespace. +bool TestChromeSubsetter() { + sfntly::ByteVector input_buffer; + sfntly::LoadFile(kInputFileName, &input_buffer); + EXPECT_GT(input_buffer.size(), (size_t)0); + + unsigned char* output_buffer = NULL; + int output_length = + SfntlyWrapper::SubsetFont(kFontName, + &(input_buffer[0]), + input_buffer.size(), + kGlyphIds, + kGlyphIdsCount, + &output_buffer); + + EXPECT_GT(output_length, 0); + + if (output_length > 0) { + FILE* output_file = NULL; +#if defined WIN32 + fopen_s(&output_file, kOutputFileName, "wb"); +#else + output_file = fopen(kOutputFileName, "wb"); +#endif + EXPECT_TRUE((output_file != NULL)); + if (output_file) { + int byte_count = fwrite(output_buffer, 1, output_length, output_file); + EXPECT_EQ(byte_count, output_length); + fflush(output_file); + fclose(output_file); + } + + delete[] output_buffer; + return true; + } + + return false; +} + +TEST(ChromeSubsetter, All) { + EXPECT_TRUE(TestChromeSubsetter()); +} diff --git a/gfx/sfntly/cpp/src/test/cmap_editing_test.cc b/gfx/sfntly/cpp/src/test/cmap_editing_test.cc new file mode 100644 index 0000000000..299b607625 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/cmap_editing_test.cc @@ -0,0 +1,101 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <map> +#include <algorithm> + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/font_header_table.h" +#include "sfntly/tag.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/port/endian.h" +#include "sfntly/port/file_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/port/refcount.h" +#include "gtest/gtest.h" + +namespace sfntly { +TEST(CMapEditingTest, RemoveAllButOneCMap) { + FontBuilderArray builders; + FontFactoryPtr font_factory; + font_factory.Attach(FontFactory::GetInstance()); + BuilderForFontFile(SAMPLE_TTF_FILE, font_factory, &builders); + ASSERT_FALSE(builders.empty()); + FontBuilderPtr font_builder = builders[0]; + Ptr<CMapTable::Builder> cmap_table_builder = + (CMapTable::Builder*)font_builder->GetTableBuilder(Tag::cmap); + ASSERT_NE(cmap_table_builder, static_cast<CMapTable::Builder*>(NULL)); + CMapTable::CMapBuilderMap* + cmap_builders = cmap_table_builder->GetCMapBuilders(); + ASSERT_FALSE(cmap_builders->empty()); + + for (CMapTable::CMapBuilderMap::iterator + it = cmap_builders->begin(); it != cmap_builders->end();) { + if (it->second->cmap_id() == CMapTable::WINDOWS_BMP) { + ++it; + } else { + cmap_builders->erase(it++); + } + } + ASSERT_EQ(cmap_builders->size(), (uint32_t)1); + Font* font = font_builder->Build(); + CMapTablePtr cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + ASSERT_EQ(1, cmap_table->NumCMaps()); + CMapTable::CMapPtr cmap; + cmap.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP)); + ASSERT_EQ(CMapTable::WINDOWS_BMP, cmap->cmap_id()); + delete font; +} + +TEST(CMapEditingTest, CopyAllCMapsToNewFont) { + FontArray fonts; + FontFactoryPtr font_factory; + font_factory.Attach(FontFactory::GetInstance()); + LoadFont(SAMPLE_TTF_FILE, font_factory, &fonts); + + ASSERT_FALSE(fonts.empty()); + ASSERT_FALSE(fonts[0] == NULL); + FontPtr font = fonts[0]; + CMapTablePtr cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + FontBuilderPtr font_builder; + font_builder.Attach(font_factory->NewFontBuilder()); + Ptr<CMapTable::Builder> cmap_table_builder = + (CMapTable::Builder*)font_builder->NewTableBuilder(Tag::cmap); + + CMapTable::CMapIterator cmap_iter(cmap_table, NULL); + while (cmap_iter.HasNext()) { + CMapTable::CMapPtr cmap; + cmap.Attach(cmap_iter.Next()); + if (!cmap) + continue; + cmap_table_builder->NewCMapBuilder(cmap->cmap_id(), cmap->ReadFontData()); + } + + FontPtr new_font; + new_font.Attach(font_builder->Build()); + CMapTablePtr new_cmap_table = + down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + ASSERT_EQ(cmap_table->NumCMaps(), new_cmap_table->NumCMaps()); + CMapTable::CMapPtr cmap; + cmap.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP)); + ASSERT_NE(cmap, static_cast<CMapTable::CMap*>(NULL)); + ASSERT_EQ(CMapTable::WINDOWS_BMP, cmap->cmap_id()); +} +} diff --git a/gfx/sfntly/cpp/src/test/cmap_iterator_test.cc b/gfx/sfntly/cpp/src/test/cmap_iterator_test.cc new file mode 100644 index 0000000000..e01a301628 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/cmap_iterator_test.cc @@ -0,0 +1,156 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> + +#include <vector> +#include <string> +#include <algorithm> + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/tag.h" +#include "sfntly/port/type.h" +#include "sfntly/port/refcount.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +#include "gtest/gtest.h" + +#if GTEST_HAS_PARAM_TEST + +namespace sfntly { +using ::testing::TestWithParam; +using ::testing::Values; + +typedef std::vector<bool> BitSet; + +class CMapIteratorTestCase { + public: + CMapIteratorTestCase(int32_t platform_id, int32_t encoding_id, + const char* file_name) + : platform_id_(platform_id), + encoding_id_(encoding_id), + file_name_(file_name) { + } + ~CMapIteratorTestCase() {} + int32_t platform_id() const { return platform_id_; } + int32_t encoding_id() const { return encoding_id_; } + const char* file_name() const { return file_name_; } + + private: + int32_t platform_id_; + int32_t encoding_id_; + const char* file_name_; +}; + +class CMapIteratorTests + : public ::testing::TestWithParam<CMapIteratorTestCase> { + public: + virtual void SetUp(); + virtual void TearDown() {} + + BitSet* GenerateCMapEntries(int32_t start, int32_t count); + int32_t CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator* + character_iterator, + BitSet* bit_set); + + Ptr<CMapTable::CMap> cmap_; +}; + +void CMapIteratorTests::SetUp() { + FontArray fonts; + Ptr<FontFactory> font_factory; + const char* file_name = GetParam().file_name(); + LoadFont(file_name, font_factory, &fonts); + Ptr<Font> font; + font.Attach(fonts[0].Detach()); + Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + ASSERT_FALSE(cmap_table == NULL); + cmap_.Attach(cmap_table->GetCMap(GetParam().platform_id(), + GetParam().encoding_id())); + ASSERT_FALSE(cmap_ == NULL); +} + +BitSet* CMapIteratorTests::GenerateCMapEntries(int32_t start, int32_t count) { + BitSet* entries = new BitSet(count); + for (int32_t c = start; c < start + count; ++c) { + int32_t g = cmap_->GlyphId(c); + if (g != CMapTable::NOTDEF) + (*entries)[c] = true; + } + return entries; +} + +int32_t +CMapIteratorTests:: +CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator* character_iterator, + BitSet* bit_set) { + int32_t iterator_not_bitset_count = 0; + BitSet::iterator end = bit_set->end(), + beginning = bit_set->begin(), + init_beginning = beginning, + current = std::find(beginning, end, true); + for (int32_t next_bit = current - beginning; + character_iterator->HasNext() && current != end; + next_bit = current - init_beginning) { + int32_t c = character_iterator->Next(); + EXPECT_TRUE(c <= next_bit || current == end); + if (!(c <= next_bit || current == end)) + return -1; + if (c == next_bit) { + beginning = current + 1; + current = std::find(beginning, end, true); + } else { + iterator_not_bitset_count++; + } + } + EXPECT_EQ(end, current); +#if defined (SFNTLY_DEBUG_CMAP) + fprintf(stderr, "%s %d: Differences between iterator and bitset: %d\n", + cmap_->format(), GetParam().file_name(), iterator_not_bitset_count); +#endif + return iterator_not_bitset_count; +} + +TEST_P(CMapIteratorTests, IteratorTest) { + BitSet* bit_set = GenerateCMapEntries(0, 0x10ffff); + CMapTable::CMap::CharacterIterator* character_iterator = NULL; + character_iterator = cmap_->Iterator(); + EXPECT_NE(character_iterator, + static_cast<CMapTable::CMap::CharacterIterator*>(NULL)); + CompareCMapIterAndBitSet(character_iterator, bit_set); + delete character_iterator; + delete bit_set; +} + +CMapIteratorTestCase kCMapIteratorTestsTestCases[] = { + CMapIteratorTestCase(CMapTable::WINDOWS_BMP.platform_id, + CMapTable::WINDOWS_BMP.encoding_id, + SAMPLE_TTF_FILE) +}; + +INSTANTIATE_TEST_CASE_P(CMapIteratorTests, + CMapIteratorTests, + ::testing::ValuesIn(kCMapIteratorTestsTestCases)); +} + +#else + +TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {} + +#endif // GTEST_HAS_PARAM diff --git a/gfx/sfntly/cpp/src/test/cmap_test.cc b/gfx/sfntly/cpp/src/test/cmap_test.cc new file mode 100644 index 0000000000..5961f1cea3 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/cmap_test.cc @@ -0,0 +1,213 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sfntly/port/type.h" +#include <assert.h> +#include <unicode/ucnv.h> + +#include <string> +#include <iostream> + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/table/core/font_header_table.h" +#include "sfntly/tag.h" + +#include "test/test_utils.h" +#include "test/test_font_utils.h" +#include "test/test_data.h" + +#if GTEST_HAS_PARAM_TEST + +namespace sfntly { +using ::testing::TestWithParam; +using ::testing::Values; + +class CMapTestCase { + public: + CMapTestCase(const char* font_name, + int32_t first_platform_id, + int32_t first_encoding_id, + const char* first_charset_name, + int32_t second_platform_id, + int32_t second_encoding_id, + const char* second_charset_name, + int32_t low_char, + int32_t high_char) + : font_name_(font_name), + first_platform_id_(first_platform_id), + first_encoding_id_(first_encoding_id), + first_charset_name_(first_charset_name), + second_platform_id_(second_platform_id), + second_encoding_id_(second_encoding_id), + second_charset_name_(second_charset_name), + low_char_(low_char), + high_char_(high_char) { + } + + const char* font_name() const { return font_name_; } + int32_t first_platform_id() const { return first_platform_id_; } + int32_t first_encoding_id() const { return first_encoding_id_; } + const char* first_charset_name() const { return first_charset_name_; } + int32_t second_platform_id() const { return second_platform_id_; } + int32_t second_encoding_id() const { return second_encoding_id_; } + const char* second_charset_name() const { return second_charset_name_; } + int32_t low_char() const { return low_char_; } + int32_t high_char() const { return high_char_; } + + private: + const char* font_name_; + int32_t first_platform_id_; + int32_t first_encoding_id_; + const char* first_charset_name_; + int32_t second_platform_id_; + int32_t second_encoding_id_; + const char* second_charset_name_; + int32_t low_char_; + int32_t high_char_; +}; + +class CMapTests : public :: testing::TestWithParam<CMapTestCase> { + public: + CMapTests() : encoder1_(NULL), encoder2_(NULL), successful_setup_(false) { + } + virtual void SetUp() {} + virtual void TearDown(); + + void CommonSetUp(FontArray* font_array); + + void CompareCMaps(); + + Ptr<CMapTable::CMap> cmap1_; + Ptr<CMapTable::CMap> cmap2_; + UConverter* encoder1_; + UConverter* encoder2_; + bool successful_setup_; +}; + +::std::ostream& operator<<(::std::ostream& os, const CMapTestCase *test_case) { + return os << "(" + << test_case->font_name() << ", " + << test_case->first_platform_id() << ", " + << test_case->first_encoding_id() << ", " + << test_case->first_charset_name() << ", " + << test_case->second_platform_id() << ", " + << test_case->second_encoding_id() << ", " + << test_case->second_charset_name() << ", " + << test_case->low_char() << ", " + << test_case->high_char() << ")"; +} + +void CMapTests::CommonSetUp(FontArray* font_array) { + ASSERT_NE(font_array, static_cast<FontArray*>(NULL)); + ASSERT_FALSE(font_array->empty()); + Ptr<Font> font; + font = font_array->at(0); + ASSERT_NE(font, static_cast<Font*>(NULL)); + Ptr<CMapTable> cmap_table = + down_cast<CMapTable*>(font->GetTable(Tag::cmap)); + cmap1_.Attach(cmap_table->GetCMap(GetParam().first_platform_id(), + GetParam().first_encoding_id())); + ASSERT_NE((cmap1_), static_cast<CMapTable::CMap*>(NULL)); + cmap2_.Attach(cmap_table->GetCMap(GetParam().second_platform_id(), + GetParam().second_encoding_id())); + ASSERT_NE((cmap2_), static_cast<CMapTable::CMap*>(NULL)); + encoder1_ = TestUtils::GetEncoder(GetParam().first_charset_name()); + encoder2_ = TestUtils::GetEncoder(GetParam().second_charset_name()); + successful_setup_ = true; +} + +void CMapTests::TearDown() { + if (encoder1_) + ucnv_close(encoder1_); + if (encoder2_) + ucnv_close(encoder2_); +} + +void CMapTests::CompareCMaps() { + ASSERT_TRUE(successful_setup_); + for (int32_t uchar = GetParam().low_char(); + uchar <= GetParam().high_char(); ++uchar) { + int32_t c1 = uchar; + if (encoder1_ != NULL) + c1 = TestUtils::EncodeOneChar(encoder1_, (int16_t)uchar); + int32_t c2 = uchar; + if (encoder2_ != NULL) + c2 = TestUtils::EncodeOneChar(encoder2_, (int16_t)uchar); + int32_t glyph_id1 = cmap1_->GlyphId(c1); + int32_t glyph_id2 = cmap2_->GlyphId(c2); +#ifdef SFNTLY_DEBUG_CMAP + if (glyph_id1 != glyph_id2) + fprintf(stderr, "%x: g1=%x, %x: g2=%x\n", c1, glyph_id1, c2, glyph_id2); +#endif + ASSERT_EQ(glyph_id1, glyph_id2); + } +#ifdef SFNTLY_SFNTLY_DEBUG_CMAPCMAP + fprintf(stderr, "\n"); +#endif +} + +TEST_P(CMapTests, GlyphsBetweenCMapsFingerprint) { + Ptr<FontFactory> font_factory; + font_factory.Attach(FontFactory::GetInstance()); + font_factory->FingerprintFont(true); + FontArray font_array; + LoadFont(GetParam().font_name(), font_factory, &font_array); + CommonSetUp(&font_array); + CompareCMaps(); +} + +TEST_P(CMapTests, GlyphsBetweenCMapsNoFingerprint) { + Ptr<FontFactory> font_factory; + font_factory.Attach(FontFactory::GetInstance()); + FontArray font_array; + LoadFont(GetParam().font_name(), font_factory, &font_array); + CommonSetUp(&font_array); + CompareCMaps(); +} + +TEST_P(CMapTests, GlyphsBetweenCMapsUsingByteVector) { + FontArray font_array; + LoadFontUsingByteVector(GetParam().font_name(), true, &font_array); + CommonSetUp(&font_array); + CompareCMaps(); +} + +CMapTestCase kCMapTestsTestCases[] = { + CMapTestCase(SAMPLE_TTF_FILE, + PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + NULL, + PlatformId::kUnicode, + UnicodeEncodingId::kUnicode2_0_BMP, + NULL, + (int32_t)0x20, + (int32_t)0x7f), +}; + +INSTANTIATE_TEST_CASE_P(CMapTests, + CMapTests, + ::testing::ValuesIn(kCMapTestsTestCases)); +} + +#else + +TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {} + +#endif // GTEST_HAS_PARAM diff --git a/gfx/sfntly/cpp/src/test/endian_test.cc b/gfx/sfntly/cpp/src/test/endian_test.cc new file mode 100644 index 0000000000..80cce6879a --- /dev/null +++ b/gfx/sfntly/cpp/src/test/endian_test.cc @@ -0,0 +1,77 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/tag.h" +#include "sfntly/data/growable_memory_byte_array.h" +#include "sfntly/data/writable_font_data.h" +#include "sfntly/math/fixed1616.h" +#include "sfntly/port/memory_output_stream.h" +#include "sfntly/data/font_output_stream.h" + +namespace sfntly { + +bool TestEndian() { + uint8_t test_data[] = { + 0x68, 0x65, 0x61, 0x64, // 0: head + 0xca, 0xca, 0xca, 0xca, // 4: ubyte, byte, char + 0x00, 0x18, 0x80, 0x18, // 8: ushort, short + 0x00, 0x00, 0x18, 0x00, // 12: uint24 + 0x00, 0x00, 0x00, 0x18, // 16: ulong + 0xff, 0xff, 0xff, 0x00, // 20: long + 0x00, 0x01, 0x00, 0x00 // 24: fixed + }; + + ByteArrayPtr ba1 = new GrowableMemoryByteArray(); + for (size_t i = 0; i < sizeof(test_data); ++i) { + ba1->Put(i, test_data[i]); + } + ReadableFontDataPtr rfd = new ReadableFontData(ba1); + EXPECT_EQ(rfd->ReadULongAsInt(0), Tag::head); + EXPECT_EQ(rfd->ReadUByte(4), 202); + EXPECT_EQ(rfd->ReadByte(5), -54); + EXPECT_EQ(rfd->ReadChar(6), 202); + EXPECT_EQ(rfd->ReadUShort(8), 24); + EXPECT_EQ(rfd->ReadShort(10), -32744); + EXPECT_EQ(rfd->ReadUInt24(12), 24); + EXPECT_EQ(rfd->ReadULong(16), 24); + EXPECT_EQ(rfd->ReadLong(20), -256); + EXPECT_EQ(rfd->ReadFixed(24), Fixed1616::Fixed(1, 0)); + + MemoryOutputStream os; + FontOutputStream fos(&os); + fos.WriteULong(Tag::head); + fos.Write(202); + fos.Write(202); + fos.Write(202); + fos.Write(202); + fos.WriteUShort(24); + fos.WriteShort(-32744); + fos.WriteUInt24(24); + fos.WriteChar(0); + fos.WriteULong(24); + fos.WriteLong(-256); + fos.WriteFixed(Fixed1616::Fixed(1, 0)); + EXPECT_EQ(memcmp(os.Get(), test_data, sizeof(test_data)), 0); + + return true; +} + +} // namespace sfntly + +TEST(Endian, All) { + ASSERT_TRUE(sfntly::TestEndian()); +} diff --git a/gfx/sfntly/cpp/src/test/file_io_test.cc b/gfx/sfntly/cpp/src/test/file_io_test.cc new file mode 100644 index 0000000000..3cec4d6900 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/file_io_test.cc @@ -0,0 +1,153 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "gtest/gtest.h" +#include "sfntly/port/file_input_stream.h" +#include "sfntly/data/font_input_stream.h" +#include "test/test_data.h" + +namespace sfntly { + +bool TestFileInputStream() { + FILE* file_handle = NULL; +#if defined (WIN32) + fopen_s(&file_handle, SAMPLE_TTF_FILE, "rb"); +#else + file_handle = fopen(SAMPLE_TTF_FILE, "rb"); +#endif + if (file_handle == NULL) { + return false; + } + fseek(file_handle, 0, SEEK_END); + size_t length = ftell(file_handle); + fseek(file_handle, 0, SEEK_SET); + ByteVector b1; + b1.resize(length); + size_t bytes_read = fread(&(b1[0]), 1, length, file_handle); + EXPECT_EQ(bytes_read, length); + fclose(file_handle); + + // Full file reading test + FileInputStream is; + is.Open(SAMPLE_TTF_FILE); + EXPECT_EQ(length, (size_t)is.Available()); + ByteVector b2; + is.Read(&b2, 0, length); + is.Close(); + EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0); + b2.clear(); + + // Partial reading test + is.Open(SAMPLE_TTF_FILE); + is.Skip(89); + is.Read(&b2, 0, 100); + EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0); + b2.clear(); + + // Skip test + is.Skip(-89); + is.Read(&b2, 0, 100); + EXPECT_EQ(memcmp(&(b1[100]), &(b2[0]), 100), 0); + b2.clear(); + is.Skip(100); + is.Read(&b2, 0, 100); + EXPECT_EQ(memcmp(&(b1[300]), &(b2[0]), 100), 0); + is.Skip(-400); + b2.clear(); + + // Offset test + is.Read(&b2, 0, 100); + is.Read(&b2, 100, 100); + EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), 200), 0); + + // Unread test + ByteVector b3; + b3.resize(200); + is.Unread(&b3); + EXPECT_EQ(memcmp(&(b3[0]), &(b2[0]), 200), 0); + + return true; +} + +bool TestFontInputStreamBasic() { + FILE* file_handle = NULL; +#if defined (WIN32) + fopen_s(&file_handle, SAMPLE_TTF_FILE, "rb"); +#else + file_handle = fopen(SAMPLE_TTF_FILE, "rb"); +#endif + if (file_handle == NULL) { + return false; + } + fseek(file_handle, 0, SEEK_END); + size_t length = ftell(file_handle); + fseek(file_handle, 0, SEEK_SET); + ByteVector b1; + b1.resize(length); + size_t bytes_read = fread(&(b1[0]), 1, length, file_handle); + EXPECT_EQ(bytes_read, length); + fclose(file_handle); + + FileInputStream is; + is.Open(SAMPLE_TTF_FILE); + FontInputStream font_is1(&is); + EXPECT_EQ((size_t)font_is1.Available(), length); + + ByteVector b2; + font_is1.Read(&b2, 0, length); + font_is1.Close(); + EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0); + b2.clear(); + + is.Open(SAMPLE_TTF_FILE); + is.Skip(89); + FontInputStream font_is2(&is, 200); + font_is2.Read(&b2, 0, 100); + EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0); + font_is2.Read(&b2, 100, 100); + EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 200), 0); + b2.clear(); + font_is2.Skip(-200); + font_is2.Read(&b2, 0, 100); + EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0); + + return true; +} + +bool TestFontInputStreamTableLoading() { + FileInputStream is; + is.Open(SAMPLE_TTF_FILE); + FontInputStream font_is(&is); + + font_is.Skip(TTF_OFFSET[SAMPLE_TTF_FEAT]); + FontInputStream gdef_is(&font_is, TTF_LENGTH[SAMPLE_TTF_FEAT]); + ByteVector feat_data; + gdef_is.Read(&feat_data, 0, TTF_LENGTH[SAMPLE_TTF_FEAT]); + EXPECT_EQ(memcmp(&(feat_data[0]), TTF_FEAT_DATA, + TTF_LENGTH[SAMPLE_TTF_FEAT]), 0); + + return true; +} + +} // namespace sfntly + +TEST(FileIO, All) { + ASSERT_TRUE(sfntly::TestFileInputStream()); + ASSERT_TRUE(sfntly::TestFontInputStreamBasic()); + ASSERT_TRUE(sfntly::TestFontInputStreamTableLoading()); +} diff --git a/gfx/sfntly/cpp/src/test/font_data_test.cc b/gfx/sfntly/cpp/src/test/font_data_test.cc new file mode 100644 index 0000000000..5ed041cb69 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/font_data_test.cc @@ -0,0 +1,337 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vector> +#include <algorithm> + +#include "gtest/gtest.h" +#include "sfntly/port/type.h" +#include "sfntly/data/writable_font_data.h" +#include "sfntly/data/memory_byte_array.h" + +namespace sfntly { + +const int32_t BYTE_ARRAY_SIZES[] = + {1, 7, 127, 128, 129, 255, 256, 257, 666, 1023, 0x10000}; + +// array data for searching +const int32_t LOWER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 4, 7, 13, 127}; +const int32_t UPPER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 5, 12, 16, 256}; +const int32_t kLowerByteArrayForSearchingLength = 5; +const int32_t kUpperByteArrayForSearchingLength = 5; + +// search test result pairs - number to search for; index found at +const int32_t SEARCH_TEST_PAIRS[][2] = { + {0, -1}, {1, -1}, {2, 0}, {3, -1}, {4, 1}, {5, 1}, {6, -1}, {12, 2}, + {13, 3}, {17, -1}, {126, -1}, {127, 4}, {256, 4}, {257, -1}, {0x1000, -1} +}; +const int32_t kSearchTestPairsLength = 15; + +// offset and start index data for searching data +// array data size, lower_start_index, lower_offset, upper_start_index, +// upper_offset +const int32_t SEARCH_TEST_OFFSETS[][5] = { + // lower[], upper[] + { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) + * DataSize::kUSHORT, + 0, + DataSize::kUSHORT, + kLowerByteArrayForSearchingLength * DataSize::kUSHORT, + DataSize::kUSHORT }, + + // {lower, upper} [] + { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) + * DataSize::kUSHORT, + 0, + 2 * DataSize::kUSHORT, + DataSize::kUSHORT, + 2 * DataSize::kUSHORT }, + + // upper[], lower[] + { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) + * DataSize::kUSHORT, + kLowerByteArrayForSearchingLength * DataSize::kUSHORT, + DataSize::kUSHORT, + 0, + DataSize::kUSHORT }, + + // {upper, lower} [] + { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength) + * DataSize::kUSHORT, + DataSize::kUSHORT, + 2 * DataSize::kUSHORT, + 0, + 2 * DataSize::kUSHORT } +}; +const int32_t kSearchTestOffsetLength = 4; + +ReadableFontData* +FillTestFontDataWithShortsForSearching(WritableFontData* wfd, + const int32_t* lower_data, + int32_t lower_start_index, + int32_t lower_offset, + const int32_t* upper_data, + int32_t upper_start_index, + int32_t upper_offset) { + // lower data + int offset = lower_start_index; + for (int32_t i = 0; i < kLowerByteArrayForSearchingLength; ++i) { + wfd->WriteUShort(offset, lower_data[i]); + offset += lower_offset; + } + + // upper data + offset = upper_start_index; + for (int32_t i = 0; i < kUpperByteArrayForSearchingLength; ++i) { + wfd->WriteUShort(offset, upper_data[i]); + offset += upper_offset; + } + + return wfd; +} + +bool TestReadableFontDataSearching() { + for (int32_t i = 0; i < kSearchTestOffsetLength; ++i) { + const int32_t* array_setup_offset = SEARCH_TEST_OFFSETS[i]; + WritableFontDataPtr wfd; + wfd.Attach(WritableFontData::CreateWritableFontData(array_setup_offset[0])); + FillTestFontDataWithShortsForSearching(wfd, + LOWER_BYTE_ARRAY_FOR_SEARCHING, + array_setup_offset[1], + array_setup_offset[2], + UPPER_BYTE_ARRAY_FOR_SEARCHING, + array_setup_offset[3], + array_setup_offset[4]); + for (int32_t j = 0; j < kSearchTestPairsLength; ++j) { + const int32_t* test_case = SEARCH_TEST_PAIRS[j]; + int32_t found = wfd->SearchUShort(array_setup_offset[1], + array_setup_offset[2], + array_setup_offset[3], + array_setup_offset[4], + kLowerByteArrayForSearchingLength, + test_case[0]); +#if defined (SFNTLY_DEBUG_FONTDATA) + fprintf(stderr, "Searching for %d; Got %d; Expected %d; " + "[test %d][offset %d]\n", + test_case[0], found, test_case[1], j, i); +#endif + EXPECT_EQ(test_case[1], found); + } + } + return true; +} + +void FillTestByteArray(ByteArray* ba, int32_t size) { + for (int32_t i = 0; i < size; ++i) { + ba->Put(i, (uint8_t)(i % 256)); + } +} + +void ReadFontDataWithSingleByte(ReadableFontData* rfd, ByteVector* buffer) { + buffer->resize(rfd->Length()); + for (int32_t index = 0; index < rfd->Length(); ++index) { + (*buffer)[index] = (uint8_t)(rfd->ReadByte(index)); + } +} + +void ReadFontDataWithBuffer(ReadableFontData* rfd, + int32_t buffer_size, + ByteVector* b) { + ByteVector buffer(buffer_size); + b->resize(rfd->Length()); + + int32_t index = 0; + while (index < rfd->Length()) { + int32_t bytes_read = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size()); + EXPECT_GE(bytes_read, 0); + std::copy(buffer.begin(), buffer.begin() + bytes_read, b->begin() + index); + index += bytes_read; + } +} + +void ReadFontDataWithSlidingWindow(ReadableFontData* rfd, int32_t window_size, + ByteVector* b) { + b->resize(rfd->Length()); + int32_t index = 0; + while (index < rfd->Length()) { + int32_t actual_window_size = + std::min<int32_t>(window_size, b->size() - index); + int32_t bytes_read = + rfd->ReadBytes(index, &((*b)[0]), index, actual_window_size); + EXPECT_GE(bytes_read, 0); + index += bytes_read; + } +} + +void WriteFontDataWithSingleByte(ReadableFontData* rfd, WritableFontData* wfd) { + for (int32_t index = 0; index < rfd->Length(); ++index) { + uint8_t b = (uint8_t)(rfd->ReadByte(index)); + wfd->WriteByte(index, b); + } +} + +void WriteFontDataWithBuffer(ReadableFontData* rfd, + WritableFontData* wfd, + int32_t buffer_size) { + ByteVector buffer(buffer_size); + int32_t index = 0; + while (index < rfd->Length()) { + int32_t bytesRead = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size()); + wfd->WriteBytes(index, &(buffer[0]), 0, buffer.size()); + index += bytesRead; + } +} + +void WriteFontDataWithSlidingWindow(ReadableFontData* rfd, + WritableFontData* wfd, + int32_t window_size) { + ByteVector b(rfd->Length()); + int32_t index = 0; + while (index < rfd->Length()) { + int32_t sliding_size = std::min<int32_t>(window_size, b.size() - index); + int32_t bytes_read = rfd->ReadBytes(index, &(b[0]), index, sliding_size); + wfd->WriteBytes(index, &(b[0]), index, sliding_size); + index += bytes_read; + } +} + +bool ReadComparison(int32_t offset, + int32_t length, + ReadableFontData* rfd1, + ReadableFontData* rfd2) { + EXPECT_TRUE(length == rfd2->Length()); + ByteVector b1, b2; + b1.resize(length); + b2.resize(length); + + // single byte reads + ReadFontDataWithSingleByte(rfd1, &b1); + ReadFontDataWithSingleByte(rfd2, &b2); + EXPECT_EQ(memcmp(&(b1[offset]), &(b2[0]), length), 0); + + // buffer reads + int32_t increments = std::max<int32_t>(length / 11, 1); + for (int32_t buffer_size = 1; buffer_size <= length; + buffer_size += increments) { + b1.clear(); + b2.clear(); + b1.resize(length); + b2.resize(length); + ReadFontDataWithBuffer(rfd1, buffer_size, &b1); + ReadFontDataWithBuffer(rfd2, buffer_size, &b2); + int result = memcmp(&(b1[offset]), &(b2[0]), length); + EXPECT_EQ(result, 0); + } + + // sliding window reads + for (int32_t window_size = 1; window_size <= length; + window_size += increments) { + b1.clear(); + b2.clear(); + b1.resize(length); + b2.resize(length); + ReadFontDataWithSlidingWindow(rfd1, window_size, &b1); + ReadFontDataWithSlidingWindow(rfd2, window_size, &b2); + int result = memcmp(&(b1[offset]), &(b2[0]), length); + EXPECT_EQ(result, 0); + } + return true; +} + +void SlicingReadTest(ReadableFontData* rfd) { + fprintf(stderr, "read - trim = "); + for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1; + trim += (rfd->Length() / 21) + 1) { + fprintf(stderr, "%d ", trim); + int32_t length = rfd->Length() - 2 * trim; + ReadableFontDataPtr slice; + slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); + EXPECT_TRUE(ReadComparison(trim, length, rfd, slice)); + } + fprintf(stderr, "\n"); +} + +void SlicingWriteTest(ReadableFontData* rfd, WritableFontData* wfd) { + fprintf(stderr, "write - trim = "); + for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1; + trim += (rfd->Length() / 21) + 1) { + fprintf(stderr, "%d ", trim); + int32_t length = rfd->Length() - 2 * trim; + WritableFontDataPtr w_slice; + ReadableFontDataPtr r_slice; + + // single byte writes + w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); + r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); + WriteFontDataWithSingleByte(r_slice, w_slice); + EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); + + // buffer writes + int32_t increments = std::max<int32_t>(length / 11, 1); + for (int32_t buffer_size = 1; buffer_size < length; + buffer_size += increments) { + w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); + r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); + WriteFontDataWithBuffer(r_slice, w_slice, buffer_size); + EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); + } + + // sliding window writes + for (int window_size = 1; window_size < length; window_size += increments) { + w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length))); + r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length))); + WriteFontDataWithSlidingWindow(r_slice, w_slice, window_size); + EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice)); + } + } + fprintf(stderr, "\n"); +} + +bool TestReadableFontData() { + for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { + int32_t size = BYTE_ARRAY_SIZES[i]; + ByteArrayPtr ba = new MemoryByteArray(size); + FillTestByteArray(ba, size); + ReadableFontDataPtr rfd = new ReadableFontData(ba); + SlicingReadTest(rfd); + } + return true; +} + +bool TestWritableFontData() { + for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) { + int32_t size = BYTE_ARRAY_SIZES[i]; + ByteArrayPtr ba = new MemoryByteArray(size); + FillTestByteArray(ba, size); + WritableFontDataPtr wfd = new WritableFontData(ba); + SlicingReadTest(wfd); + ByteArrayPtr temp = new MemoryByteArray(size); + WritableFontDataPtr wfd_copy = new WritableFontData(temp); + SlicingWriteTest(wfd, wfd_copy); + } + return true; +} + +} // namespace sfntly + +TEST(FontData, ReadableFontDataSearching) { + ASSERT_TRUE(sfntly::TestReadableFontDataSearching()); +} + +TEST(FontData, All) { + ASSERT_TRUE(sfntly::TestReadableFontData()); + ASSERT_TRUE(sfntly::TestWritableFontData()); +} diff --git a/gfx/sfntly/cpp/src/test/font_parsing_test.cc b/gfx/sfntly/cpp/src/test/font_parsing_test.cc new file mode 100644 index 0000000000..6fd5c3b29e --- /dev/null +++ b/gfx/sfntly/cpp/src/test/font_parsing_test.cc @@ -0,0 +1,140 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "sfntly/data/font_input_stream.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/font_header_table.h" +#include "sfntly/table/table.h" +#include "sfntly/table/generic_table_builder.h" +#include "sfntly/table/table_based_table_builder.h" +#include "sfntly/tag.h" +#include "sfntly/port/file_input_stream.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +bool TestFontParsing() { + ByteVector input_buffer; + LoadFile(SAMPLE_TTF_FILE, &input_buffer); + + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + // File based + FontBuilderArray font_builder_array; + BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + // Memory based + FontBuilderArray font_builder_array2; + factory->LoadFontsForBuilding(&input_buffer, &font_builder_array2); + FontBuilderPtr font_builder2 = font_builder_array2[0]; + + for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) { + EXPECT_TRUE(font_builder->HasTableBuilder(TTF_KNOWN_TAGS[i])); + EXPECT_TRUE(font_builder2->HasTableBuilder(TTF_KNOWN_TAGS[i])); + } + + // Generic table + Ptr<GenericTableBuilder> gdef_builder = + down_cast<GenericTableBuilder*>(font_builder->GetTableBuilder(Tag::feat)); + HeaderPtr gdef_header = gdef_builder->header(); + EXPECT_EQ(gdef_header->length(), TTF_LENGTH[SAMPLE_TTF_FEAT]); + EXPECT_EQ(gdef_header->offset(), TTF_OFFSET[SAMPLE_TTF_FEAT]); + EXPECT_EQ(gdef_header->checksum(), TTF_CHECKSUM[SAMPLE_TTF_FEAT]); + EXPECT_TRUE(gdef_header->checksum_valid()); + + WritableFontDataPtr wfd; + wfd.Attach(gdef_builder->Data()); + ByteVector b; + b.resize(TTF_LENGTH[SAMPLE_TTF_FEAT]); + wfd->ReadBytes(0, &(b[0]), 0, TTF_LENGTH[SAMPLE_TTF_FEAT]); + EXPECT_EQ(memcmp(&(b[0]), TTF_FEAT_DATA, TTF_LENGTH[SAMPLE_TTF_FEAT]), 0); + + // Header table + FontHeaderTableBuilderPtr header_builder = + down_cast<FontHeaderTable::Builder*>( + font_builder->GetTableBuilder(Tag::head)); + HeaderPtr header_header = header_builder->header(); + EXPECT_EQ(header_header->length(), TTF_LENGTH[SAMPLE_TTF_HEAD]); + EXPECT_EQ(header_header->offset(), TTF_OFFSET[SAMPLE_TTF_HEAD]); + EXPECT_EQ(header_header->checksum(), TTF_CHECKSUM[SAMPLE_TTF_HEAD]); + EXPECT_TRUE(header_header->checksum_valid()); + + // Data conformance + for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) { + ByteVector b1, b2; + b1.resize(TTF_LENGTH[i]); + b2.resize(TTF_LENGTH[i]); + TableBuilderPtr builder1 = + font_builder->GetTableBuilder(TTF_KNOWN_TAGS[i]); + TableBuilderPtr builder2 = + font_builder2->GetTableBuilder(TTF_KNOWN_TAGS[i]); + WritableFontDataPtr wfd1; + wfd1.Attach(builder1->Data()); + WritableFontDataPtr wfd2; + wfd2.Attach(builder2->Data()); + wfd1->ReadBytes(0, &(b1[0]), 0, TTF_LENGTH[i]); + wfd2->ReadBytes(0, &(b2[0]), 0, TTF_LENGTH[i]); + EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), TTF_LENGTH[i]), 0); + } + + return true; +} + +bool TestTTFReadWrite() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + FontPtr font; + font.Attach(font_builder->Build()); + MemoryOutputStream output_stream; + factory->SerializeFont(font, &output_stream); + EXPECT_GE(output_stream.Size(), SAMPLE_TTF_SIZE); + + return true; +} + +bool TestTTFMemoryBasedReadWrite() { + ByteVector input_buffer; + LoadFile(SAMPLE_TTF_FILE, &input_buffer); + + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + factory->LoadFontsForBuilding(&input_buffer, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + FontPtr font; + font.Attach(font_builder->Build()); + MemoryOutputStream output_stream; + factory->SerializeFont(font, &output_stream); + EXPECT_GE(output_stream.Size(), input_buffer.size()); + + return true; +} + +} // namespace sfntly + +TEST(FontParsing, All) { + ASSERT_TRUE(sfntly::TestFontParsing()); + ASSERT_TRUE(sfntly::TestTTFReadWrite()); + ASSERT_TRUE(sfntly::TestTTFMemoryBasedReadWrite()); +} diff --git a/gfx/sfntly/cpp/src/test/hdmx_test.cc b/gfx/sfntly/cpp/src/test/hdmx_test.cc new file mode 100644 index 0000000000..cdc4ed0587 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/hdmx_test.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/core/horizontal_device_metrics_table.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +const int32_t HDMX_VERSION = 0; +const int32_t HDMX_NUM_RECORDS = 4; +const int32_t HDMX_RECORD_SIZE = 628; +const int32_t HDMX_PIXEL_SIZE[] = {10, 11, 12, 13}; +const int32_t HDMX_MAX_WIDTH[] = {5, 6, 7, 7}; + +bool TestReadingHdmxTable() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontArray font_array; + LoadFont(SAMPLE_BITMAP_FONT, factory, &font_array); + FontPtr font = font_array[0]; + + HorizontalDeviceMetricsTablePtr hdmx_table = + down_cast<HorizontalDeviceMetricsTable*>(font->GetTable(Tag::hdmx)); + + EXPECT_FALSE(hdmx_table == NULL); + + EXPECT_EQ(hdmx_table->Version(), HDMX_VERSION); + EXPECT_EQ(hdmx_table->NumRecords(), HDMX_NUM_RECORDS); + EXPECT_EQ(hdmx_table->RecordSize(), HDMX_RECORD_SIZE); + + for (int32_t i = 0; i < HDMX_NUM_RECORDS; ++i) { + EXPECT_EQ(hdmx_table->PixelSize(i), HDMX_PIXEL_SIZE[i]); + EXPECT_EQ(hdmx_table->MaxWidth(i), HDMX_MAX_WIDTH[i]); + } + + EXPECT_EQ(hdmx_table->Width(0, 0), HDMX_MAX_WIDTH[0]); + EXPECT_EQ(hdmx_table->Width(0, 19), HDMX_MAX_WIDTH[0]); + EXPECT_EQ(hdmx_table->Width(0, 623), HDMX_MAX_WIDTH[0]); + EXPECT_EQ(hdmx_table->Width(1, 0), HDMX_MAX_WIDTH[1]); + EXPECT_EQ(hdmx_table->Width(1, 19), HDMX_MAX_WIDTH[1]); + EXPECT_EQ(hdmx_table->Width(1, 623), HDMX_MAX_WIDTH[1]); + EXPECT_EQ(hdmx_table->Width(2, 0), HDMX_MAX_WIDTH[2]); + EXPECT_EQ(hdmx_table->Width(2, 19), HDMX_MAX_WIDTH[2]); + EXPECT_EQ(hdmx_table->Width(2, 623), HDMX_MAX_WIDTH[2]); + EXPECT_EQ(hdmx_table->Width(3, 0), HDMX_MAX_WIDTH[3]); + EXPECT_EQ(hdmx_table->Width(3, 19), HDMX_MAX_WIDTH[3]); + EXPECT_EQ(hdmx_table->Width(3, 623), HDMX_MAX_WIDTH[3]); + +#if defined(SFNTLY_NO_EXCEPTION) + EXPECT_EQ(hdmx_table->PixelSize(4), -1); + EXPECT_EQ(hdmx_table->PixelSize(-1), -1); + EXPECT_EQ(hdmx_table->MaxWidth(4), -1); + EXPECT_EQ(hdmx_table->MaxWidth(-1), -1); + EXPECT_EQ(hdmx_table->Width(0, 624), -1); + EXPECT_EQ(hdmx_table->Width(1, -1), -1); + EXPECT_EQ(hdmx_table->Width(-1, 0), -1); + EXPECT_EQ(hdmx_table->Width(-1, -1), -1); +#endif + return true; +} + +} // namespace sfntly + +TEST(HdmxTable, All) { + ASSERT_TRUE(sfntly::TestReadingHdmxTable()); +} diff --git a/gfx/sfntly/cpp/src/test/lock_test.cc b/gfx/sfntly/cpp/src/test/lock_test.cc new file mode 100644 index 0000000000..b29a4bf7ab --- /dev/null +++ b/gfx/sfntly/cpp/src/test/lock_test.cc @@ -0,0 +1,244 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> + +#include "gtest/gtest.h" +#include "sfntly/port/lock.h" +#include "test/platform_thread.h" + +namespace sfntly { + +// Basic test to make sure that Acquire()/Unlock()/Try() don't crash + +class BasicLockTestThread : public PlatformThread::Delegate { + public: + BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {} + + virtual void ThreadMain() { + for (int i = 0; i < 10; i++) { + lock_->Acquire(); + acquired_++; + lock_->Unlock(); + } + for (int i = 0; i < 10; i++) { + lock_->Acquire(); + acquired_++; + PlatformThread::Sleep(rand() % 20); + lock_->Unlock(); + } + for (int i = 0; i < 10; i++) { + if (lock_->Try()) { + acquired_++; + PlatformThread::Sleep(rand() % 20); + lock_->Unlock(); + } + } + } + + int acquired() const { return acquired_; } + + private: + Lock* lock_; + int acquired_; + + NO_COPY_AND_ASSIGN(BasicLockTestThread); +}; + +bool BasicLockTest() { + Lock lock; + BasicLockTestThread thread(&lock); + PlatformThreadHandle handle = kNullThreadHandle; + + EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); + + int acquired = 0; + for (int i = 0; i < 5; i++) { + lock.Acquire(); + acquired++; + lock.Unlock(); + } + for (int i = 0; i < 10; i++) { + lock.Acquire(); + acquired++; + PlatformThread::Sleep(rand() % 20); + lock.Unlock(); + } + for (int i = 0; i < 10; i++) { + if (lock.Try()) { + acquired++; + PlatformThread::Sleep(rand() % 20); + lock.Unlock(); + } + } + for (int i = 0; i < 5; i++) { + lock.Acquire(); + acquired++; + PlatformThread::Sleep(rand() % 20); + lock.Unlock(); + } + + PlatformThread::Join(handle); + + EXPECT_GE(acquired, 20); + EXPECT_GE(thread.acquired(), 20); + + return true; +} + +// Test that Try() works as expected ------------------------------------------- + +class TryLockTestThread : public PlatformThread::Delegate { + public: + TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {} + + virtual void ThreadMain() { + got_lock_ = lock_->Try(); + if (got_lock_) + lock_->Unlock(); + } + + bool got_lock() const { return got_lock_; } + + private: + Lock* lock_; + bool got_lock_; + + NO_COPY_AND_ASSIGN(TryLockTestThread); +}; + +bool TryLockTest() { + Lock lock; + + EXPECT_TRUE(lock.Try()); + // We now have the lock.... + + // This thread will not be able to get the lock. + { + TryLockTestThread thread(&lock); + PlatformThreadHandle handle = kNullThreadHandle; + + EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); + + PlatformThread::Join(handle); + + EXPECT_FALSE(thread.got_lock()); + } + + lock.Unlock(); + + // This thread will.... + { + TryLockTestThread thread(&lock); + PlatformThreadHandle handle = kNullThreadHandle; + + EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); + + PlatformThread::Join(handle); + + EXPECT_TRUE(thread.got_lock()); + // But it released it.... + EXPECT_TRUE(lock.Try()); + } + + lock.Unlock(); + return true; +} + +// Tests that locks actually exclude ------------------------------------------- + +class MutexLockTestThread : public PlatformThread::Delegate { + public: + MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {} + + // Static helper which can also be called from the main thread. + static void DoStuff(Lock* lock, int* value) { + for (int i = 0; i < 40; i++) { + lock->Acquire(); + int v = *value; + PlatformThread::Sleep(rand() % 10); + *value = v + 1; + lock->Unlock(); + } + } + + virtual void ThreadMain() { + DoStuff(lock_, value_); + } + + private: + Lock* lock_; + int* value_; + + NO_COPY_AND_ASSIGN(MutexLockTestThread); +}; + +bool MutexTwoThreads() { + Lock lock; + int value = 0; + + MutexLockTestThread thread(&lock, &value); + PlatformThreadHandle handle = kNullThreadHandle; + + EXPECT_TRUE(PlatformThread::Create(&thread, &handle)); + + MutexLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle); + + EXPECT_EQ(2 * 40, value); + return true; +} + +bool MutexFourThreads() { + Lock lock; + int value = 0; + + MutexLockTestThread thread1(&lock, &value); + MutexLockTestThread thread2(&lock, &value); + MutexLockTestThread thread3(&lock, &value); + PlatformThreadHandle handle1 = kNullThreadHandle; + PlatformThreadHandle handle2 = kNullThreadHandle; + PlatformThreadHandle handle3 = kNullThreadHandle; + + EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1)); + EXPECT_TRUE(PlatformThread::Create(&thread2, &handle2)); + EXPECT_TRUE(PlatformThread::Create(&thread3, &handle3)); + + MutexLockTestThread::DoStuff(&lock, &value); + + PlatformThread::Join(handle1); + PlatformThread::Join(handle2); + PlatformThread::Join(handle3); + + EXPECT_EQ(4 * 40, value); + return true; +} + +} // namespace sfntly + +TEST(LockTest, Basic) { + ASSERT_TRUE(sfntly::BasicLockTest()); +} + +TEST(LockTest, TryLock) { + ASSERT_TRUE(sfntly::TryLockTest()); +} + +TEST(LockTest, Mutex) { + ASSERT_TRUE(sfntly::MutexTwoThreads()); + ASSERT_TRUE(sfntly::MutexFourThreads()); +} diff --git a/gfx/sfntly/cpp/src/test/memory_io_test.cc b/gfx/sfntly/cpp/src/test/memory_io_test.cc new file mode 100755 index 0000000000..b34e1e74f9 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/memory_io_test.cc @@ -0,0 +1,102 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include <algorithm> + +#include "gtest/gtest.h" +#include "sfntly/port/memory_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "sfntly/port/type.h" + +namespace { + const char* kTestData = +"01234567890123456789012345678901234567890123456789" // 50 +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx" // 100 +"yz"; // 102 + const size_t kTestBufferLen = 102; +} + +namespace sfntly { + +bool TestMemoryInputStream() { + ByteVector test_buffer; + test_buffer.resize(kTestBufferLen); + std::copy(kTestData, kTestData + kTestBufferLen, test_buffer.begin()); + + MemoryInputStream is; + is.Attach(&(test_buffer[0]), kTestBufferLen); + EXPECT_EQ(is.Available(), (int32_t)kTestBufferLen); + + // Read one byte + EXPECT_EQ(is.Read(), '0'); // position 1 + EXPECT_EQ(is.Read(), '1'); // position 2 + EXPECT_EQ(is.Read(), '2'); // position 3 + + // Read byte vector + ByteVector b; + b.resize(7); + EXPECT_EQ(is.Read(&b), 7); // position 10 + EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 3, 7), 0); + + b.resize(17); + EXPECT_EQ(is.Read(&b, 7, 10), 10); // position 20 + EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 3, 17), 0); + + // Test skip + b.clear(); + b.resize(10); + EXPECT_EQ(is.Skip(30), 30); // position 50 + EXPECT_EQ(is.Read(&b), 10); // position 60 + EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 50, 10), 0); + b.clear(); + b.resize(10); + EXPECT_EQ(is.Skip(-20), -20); // position 40 + EXPECT_EQ(is.Read(&b), 10); // position 50 + EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 40, 10), 0); + + EXPECT_EQ(is.Available(), (int32_t)kTestBufferLen - 50); + EXPECT_EQ(is.Skip(-60), -50); // Out of bound, position 0 + EXPECT_EQ(is.Skip(kTestBufferLen + 10), (int32_t)kTestBufferLen); + + b.clear(); + b.resize(10); + is.Unread(&b); + EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + kTestBufferLen - 10, 10), 0); + + return true; +} + +bool TestMemoryOutputStream() { + ByteVector test_buffer; + test_buffer.resize(kTestBufferLen); + std::copy(kTestData, kTestData + kTestBufferLen, test_buffer.begin()); + + MemoryOutputStream os; + os.Write(&(test_buffer[0]), (int32_t)50, (int32_t)(kTestBufferLen - 50)); + EXPECT_EQ(os.Size(), kTestBufferLen - 50); + EXPECT_EQ(memcmp(os.Get(), &(test_buffer[0]) + 50, kTestBufferLen - 50), 0); + + return true; +} + +} // namespace sfntly + +TEST(MemoryIO, All) { + ASSERT_TRUE(sfntly::TestMemoryInputStream()); + ASSERT_TRUE(sfntly::TestMemoryOutputStream()); +} diff --git a/gfx/sfntly/cpp/src/test/name_editing_test.cc b/gfx/sfntly/cpp/src/test/name_editing_test.cc new file mode 100644 index 0000000000..260d9d4ae1 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/name_editing_test.cc @@ -0,0 +1,242 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Must include this before ICU to avoid stdint redefinition issue. +#include "sfntly/port/type.h" + +#include <unicode/ustring.h> +#include <unicode/unistr.h> + +#include "gtest/gtest.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/port/memory_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "sfntly/table/core/name_table.h" +#include "sfntly/tag.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +static ByteVector input_buffer; + +void LoadTestFile(FontFactory* factory, FontBuilderArray* font_builders) { + assert(factory); + assert(font_builders); + if (input_buffer.empty()) { + LoadFile(SAMPLE_TTF_FILE, &input_buffer); + } + factory->LoadFontsForBuilding(&input_buffer, font_builders); +} + +bool TestChangeOneName() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + LoadTestFile(factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>( + font_builder->GetTableBuilder(Tag::name)); + + // Change the font name. + NameEntryBuilderPtr neb = + name_builder->NameBuilder(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + U_STRING_DECL(new_name, "Timothy", 7); + neb->SetName(new_name); + + // Build the font. + FontPtr font; + font.Attach(font_builder->Build()); + + // Serialize and reload the serialized font. + MemoryOutputStream os; + factory->SerializeFont(font, &os); + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + FontArray font_array; + factory->LoadFonts(&is, &font_array); + FontPtr new_font = font_array[0]; + + // Check the font name. + NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); + UChar* name = name_table->Name(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + EXPECT_TRUE(name != NULL); + EXPECT_EQ(u_strcmp(name, new_name), 0); + delete[] name; + return true; +} + +bool TestModifyNameTableAndRevert() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + LoadTestFile(factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>( + font_builder->GetTableBuilder(Tag::name)); + + // Change the font name. + NameEntryBuilderPtr neb = + name_builder->NameBuilder(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + NameTable::NameEntry* neb_entry = neb->name_entry(); + UChar* original_name = neb_entry->Name(); + EXPECT_TRUE(original_name != NULL); + + U_STRING_DECL(new_name, "Timothy", 7); + neb->SetName(new_name); + name_builder->RevertNames(); + + // Build the font. + FontPtr font; + font.Attach(font_builder->Build()); + + // Serialize and reload the serialized font. + MemoryOutputStream os; + factory->SerializeFont(font, &os); + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + FontArray font_array; + factory->LoadFonts(&is, &font_array); + FontPtr new_font = font_array[0]; + + // Check the font name. + NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); + UChar* name = name_table->Name(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + + EXPECT_EQ(u_strcmp(name, original_name), 0); + delete[] name; + delete[] original_name; + + return true; +} + +bool TestRemoveOneName() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + LoadTestFile(factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>( + font_builder->GetTableBuilder(Tag::name)); + + EXPECT_TRUE(name_builder->Has(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName)); + EXPECT_TRUE(name_builder->Remove(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName)); + + // Build the font. + FontPtr font; + font.Attach(font_builder->Build()); + + // Serialize and reload the serialized font. + MemoryOutputStream os; + factory->SerializeFont(font, &os); + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + FontArray font_array; + factory->LoadFonts(&is, &font_array); + FontPtr new_font = font_array[0]; + + // Check the font name. + NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); + UChar* name = name_table->Name(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + EXPECT_TRUE(name == NULL); + + return true; +} + +// Note: Function is not implemented but the test case is built. Uncomment +// when NameTable::clear() is implemented. +/* +bool TestClearAllNamesAndSetOne() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + LoadTestFile(factory, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>( + font_builder->GetTableBuilder(Tag::name)); + + EXPECT_GT(name_builder->builderCount(), 0); + name_builder->clear(); + EXPECT_EQ(name_builder->builderCount(), 0); + + // Change the font name. + NameEntryBuilderPtr neb = + name_builder->NameBuilder(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + U_STRING_DECL(new_name, "Fred", 4); + neb->SetName(new_name); + + // Build the font. + FontPtr font = font_builder->Build(); + + // Serialize and reload the serialized font. + MemoryOutputStream os; + factory->SerializeFont(font, &os); + FontArray font_array; + ByteArrayPtr new_ba = new MemoryByteArray(os.Get(), os.Size()); + factory->LoadFonts(new_ba, &font_array); + FontPtr new_font = font_array[0]; + + // Check the font name. + NameTablePtr name_table = down_cast<NameTable*>(font->table(Tag::name)); + UChar* name = name_table->Name(PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2, + WindowsLanguageId::kEnglish_UnitedStates, + NameId::kFontFamilyName); + EXPECT_EQ(name_table->NameCount(), 1); + EXPECT_EQ(u_strcmp(name, new_name), 0); + + delete[] name; + return true; +} +*/ + +} // namespace sfntly + +TEST(NameEditing, All) { + EXPECT_TRUE(sfntly::TestChangeOneName()); + EXPECT_TRUE(sfntly::TestModifyNameTableAndRevert()); + EXPECT_TRUE(sfntly::TestRemoveOneName()); +} diff --git a/gfx/sfntly/cpp/src/test/open_type_data_test.cc b/gfx/sfntly/cpp/src/test/open_type_data_test.cc new file mode 100644 index 0000000000..5d73e84d9a --- /dev/null +++ b/gfx/sfntly/cpp/src/test/open_type_data_test.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/data/writable_font_data.h" +#include "sfntly/data/memory_byte_array.h" + +namespace sfntly { + +const uint8_t TEST_OTF_DATA[] = + {0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + +bool TestOTFRead() { + ByteVector bytes; + for (size_t i = 0; i < sizeof(TEST_OTF_DATA) / sizeof(uint8_t); ++i) { + bytes.push_back(TEST_OTF_DATA[i]); + } + ByteArrayPtr array = new MemoryByteArray(&(bytes[0]), bytes.size()); + ReadableFontDataPtr data = new ReadableFontData(array); + + EXPECT_EQ(-1, data->ReadByte(0)); + EXPECT_EQ(0xff, data->ReadUByte(0)); + EXPECT_EQ(0x01, data->ReadByte(1)); + EXPECT_EQ(65281, data->ReadUShort(0)); + EXPECT_EQ(-255, data->ReadShort(0)); + EXPECT_EQ(16711937, data->ReadUInt24(0)); + EXPECT_EQ(4278255873LL, data->ReadULong(0)); + EXPECT_EQ(-16711423, data->ReadLong(0)); + return true; +} + +bool TestOTFCopy() { + ByteVector source_bytes(1024); + for (size_t i = 0; i < source_bytes.size(); ++i) { + source_bytes[i] = (uint8_t)(i & 0xff); + } + ByteArrayPtr source_array = new MemoryByteArray(&(source_bytes[0]), 1024); + ReadableFontDataPtr source = new ReadableFontData(source_array); + + ByteVector destination_bytes(1024); + ByteArrayPtr destination_array = + new MemoryByteArray(&(destination_bytes[0]), 1024); + WritableFontDataPtr destination = new WritableFontData(destination_array); + + int32_t length = source->CopyTo(destination); + EXPECT_EQ(1024, length); + EXPECT_TRUE(std::equal(source_bytes.begin(), source_bytes.end(), + destination_bytes.begin())); + return true; +} + +} // namespace sfntly + +TEST(OpenTypeData, All) { + ASSERT_TRUE(sfntly::TestOTFRead()); + ASSERT_TRUE(sfntly::TestOTFCopy()); +} diff --git a/gfx/sfntly/cpp/src/test/otf_basic_editing_test.cc b/gfx/sfntly/cpp/src/test/otf_basic_editing_test.cc new file mode 100644 index 0000000000..04c8778c25 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/otf_basic_editing_test.cc @@ -0,0 +1,101 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/font_header_table.h" +#include "sfntly/tag.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/port/endian.h" +#include "sfntly/port/file_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +bool TestOTFBasicEditing() { + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array); + if (font_builder_array.size() != 1) { + EXPECT_TRUE(false); + return false; + } + FontBuilderPtr font_builder = font_builder_array[0]; + + // ensure the builder is not bogus + if (!font_builder) { + EXPECT_TRUE(false); + return false; + } + TableBuilderMap* builder_map = font_builder->table_builders(); + if (!builder_map) { + EXPECT_TRUE(false); + return false; + } + IntegerSet builder_tags; + for (TableBuilderMap::iterator i = builder_map->begin(), + e = builder_map->end(); i != e; ++i) { + if (!i->second) { + EXPECT_TRUE(false); + char tag[5] = {0}; + int32_t value = ToBE32(i->first); + memcpy(tag, &value, 4); + fprintf(stderr, "tag %s does not have valid builder\n", tag); + continue; + } + builder_tags.insert(i->first); + } + + FontHeaderTableBuilderPtr header_builder = + down_cast<FontHeaderTable::Builder*>( + font_builder->GetTableBuilder(Tag::head)); + int64_t mod_date = header_builder->Modified(); + EXPECT_EQ(3397043097, mod_date); + header_builder->SetModified(mod_date + 1); + FontPtr font; + font.Attach(font_builder->Build()); + + // ensure every table had a builder + const TableMap* table_map = font->GetTableMap(); + for (TableMap::const_iterator i = table_map->begin(), e = table_map->end(); + i != e; ++i) { + TablePtr table = i->second; + HeaderPtr header = table->header(); + size_t erased = builder_tags.erase(header->tag()); + EXPECT_EQ(1U, erased); + } + EXPECT_TRUE(builder_tags.empty()); + + FontHeaderTablePtr header = + down_cast<FontHeaderTable*>(font->GetTable(Tag::head)); + int64_t after_mod_date = header->Modified(); + EXPECT_EQ(mod_date + 1, after_mod_date); + + // Checksum correctness of builder. + TablePtr post = font->GetTable(Tag::post); + EXPECT_EQ(TTF_CHECKSUM[SAMPLE_TTF_POST], post->CalculatedChecksum()); + return true; +} + +} // namespace sfntly + +TEST(OTFBasicEditing, All) { + ASSERT_TRUE(sfntly::TestOTFBasicEditing()); +} diff --git a/gfx/sfntly/cpp/src/test/platform_thread.cc b/gfx/sfntly/cpp/src/test/platform_thread.cc new file mode 100644 index 0000000000..6a0b84b4a7 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/platform_thread.cc @@ -0,0 +1,101 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test/platform_thread.h" + +namespace sfntly { + +#if defined (WIN32) + +DWORD __stdcall ThreadFunc(void* params) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(params); + delegate->ThreadMain(); + return 0; +} + +// static +bool PlatformThread::Create(Delegate* delegate, + PlatformThreadHandle* thread_handle) { + assert(thread_handle); + *thread_handle = CreateThread(NULL, 0, ThreadFunc, delegate, 0, NULL); + if (!(*thread_handle)) { + return false; + } + + return true; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + assert(thread_handle); + DWORD result = WaitForSingleObject(thread_handle, INFINITE); + assert(result == WAIT_OBJECT_0); + CloseHandle(thread_handle); +} + +// static +void PlatformThread::Sleep(int32_t duration_ms) { + ::Sleep(duration_ms); +} + +#else + +void* ThreadFunc(void* params) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(params); + delegate->ThreadMain(); + return NULL; +} + +// static +bool PlatformThread::Create(Delegate* delegate, + PlatformThreadHandle* thread_handle) { + assert(thread_handle); + + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate); + pthread_attr_destroy(&attributes); + + return success; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + assert(thread_handle); + pthread_join(thread_handle, NULL); +} + +// static +void PlatformThread::Sleep(int32_t duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +#endif // WIN32 + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/platform_thread.h b/gfx/sfntly/cpp/src/test/platform_thread.h new file mode 100644 index 0000000000..f236f4cdf3 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/platform_thread.h @@ -0,0 +1,75 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Simple platform thread implementation used to test our cross-platform locks. +// This is a trimmed down version of Chromium base/threading/platform_thread.h. + +#ifndef SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_ +#define SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_ + +#if defined (WIN32) +#include <windows.h> +#else // Assume pthread +#include <errno.h> +#include <pthread.h> +#include <time.h> +#endif // if defined (WIN32) + +#include "sfntly/port/type.h" + +namespace sfntly { + +#if defined (WIN32) +typedef HANDLE PlatformThreadHandle; +const PlatformThreadHandle kNullThreadHandle = NULL; +#else // Assume pthread +typedef pthread_t PlatformThreadHandle; +const PlatformThreadHandle kNullThreadHandle = 0; +#endif + +class PlatformThread { + public: + class Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // Sleeps for the specified duration (units are milliseconds). + static void Sleep(int32_t duration_ms); + + // Creates a new thread using default stack size. Upon success, + // |*thread_handle| will be assigned a handle to the newly created thread, + // and |delegate|'s ThreadMain method will be executed on the newly created + // thread. + // NOTE: When you are done with the thread handle, you must call Join to + // release system resources associated with the thread. You must ensure that + // the Delegate object outlives the thread. + static bool Create(Delegate* delegate, PlatformThreadHandle* thread_handle); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + +private: + PlatformThread() {} + NO_COPY_AND_ASSIGN(PlatformThread); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_ diff --git a/gfx/sfntly/cpp/src/test/serialization_test.cc b/gfx/sfntly/cpp/src/test/serialization_test.cc new file mode 100755 index 0000000000..0f2f489f4b --- /dev/null +++ b/gfx/sfntly/cpp/src/test/serialization_test.cc @@ -0,0 +1,151 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/port/memory_input_stream.h" +#include "sfntly/port/memory_output_stream.h" +#include "test/test_data.h" +#include "test/test_font_utils.h" +#include "test/serialization_test.h" + +namespace sfntly { + +bool TestSerialization() { + FontFactoryPtr factory1, factory2, factory3; + factory1.Attach(FontFactory::GetInstance()); + FontArray font_array; + LoadFont(SAMPLE_TTF_FILE, factory1, &font_array); + FontPtr original = font_array[0]; + + factory2.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + BuilderForFontFile(SAMPLE_TTF_FILE, factory2, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + FontPtr intermediate; + intermediate.Attach(font_builder->Build()); + MemoryOutputStream os; + factory2->SerializeFont(intermediate, &os); + + factory3.Attach(FontFactory::GetInstance()); + FontArray new_font_array; + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + factory3->LoadFonts(&is, &new_font_array); + FontPtr serialized = new_font_array[0]; + + // Check number of tables + EXPECT_EQ(original->num_tables(), serialized->num_tables()); + + // Check if same set of tables + const TableMap* original_tables = original->GetTableMap(); + const TableMap* serialized_tables = serialized->GetTableMap(); + EXPECT_EQ(original_tables->size(), serialized_tables->size()); + TableMap::const_iterator not_found = serialized_tables->end(); + for (TableMap::const_iterator b = original_tables->begin(), + e = original_tables->end(); b != e; ++b) { + EXPECT_TRUE((serialized_tables->find(b->first) != not_found)); + } + + // TODO(arthurhsu): check cmap equivalence + // Check checksum equivalence + for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) { + TablePtr original_table = original->GetTable(TTF_KNOWN_TAGS[i]); + TablePtr serialized_table = serialized->GetTable(TTF_KNOWN_TAGS[i]); + EXPECT_EQ(original_table->CalculatedChecksum(), + serialized_table->CalculatedChecksum()); + EXPECT_EQ(original_table->DataLength(), serialized_table->DataLength()); + + if (TTF_KNOWN_TAGS[i] == Tag::hhea) { + EXPECT_TRUE(VerifyHHEA(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::glyf) { + EXPECT_TRUE(VerifyGLYF(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::hmtx) { + EXPECT_TRUE(VerifyHMTX(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::loca) { + EXPECT_TRUE(VerifyLOCA(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::maxp) { + EXPECT_TRUE(VerifyMAXP(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::name) { + EXPECT_TRUE(VerifyNAME(original_table, serialized_table)); + } else if (TTF_KNOWN_TAGS[i] == Tag::OS_2) { + EXPECT_TRUE(VerifyOS_2(original_table, serialized_table)); + } + } + + return true; +} + +bool TestSerializationBitmap() { + FontFactoryPtr factory1, factory2, factory3; + factory1.Attach(FontFactory::GetInstance()); + FontArray font_array; + LoadFont(SAMPLE_BITMAP_FONT, factory1, &font_array); + FontPtr original = font_array[0]; + + factory2.Attach(FontFactory::GetInstance()); + FontBuilderArray font_builder_array; + BuilderForFontFile(SAMPLE_BITMAP_FONT, factory2, &font_builder_array); + FontBuilderPtr font_builder = font_builder_array[0]; + + FontPtr intermediate; + intermediate.Attach(font_builder->Build()); + MemoryOutputStream os; + factory2->SerializeFont(intermediate, &os); + + factory3.Attach(FontFactory::GetInstance()); + FontArray new_font_array; + MemoryInputStream is; + is.Attach(os.Get(), os.Size()); + factory3->LoadFonts(&is, &new_font_array); + FontPtr serialized = new_font_array[0]; + + // Check number of tables + EXPECT_EQ(original->num_tables(), serialized->num_tables()); + + // Check if same set of tables + const TableMap* original_tables = original->GetTableMap(); + const TableMap* serialized_tables = serialized->GetTableMap(); + EXPECT_EQ(original_tables->size(), serialized_tables->size()); + TableMap::const_iterator not_found = serialized_tables->end(); + for (TableMap::const_iterator b = original_tables->begin(), + e = original_tables->end(); b != e; ++b) { + EXPECT_TRUE((serialized_tables->find(b->first) != not_found)); + } + + // Check checksum equivalence + for (size_t i = 0; i < SAMPLE_BITMAP_KNOWN_TAGS; ++i) { + TablePtr original_table = original->GetTable(BITMAP_KNOWN_TAGS[i]); + TablePtr serialized_table = serialized->GetTable(BITMAP_KNOWN_TAGS[i]); + EXPECT_EQ(original_table->CalculatedChecksum(), + serialized_table->CalculatedChecksum()); + EXPECT_EQ(original_table->DataLength(), serialized_table->DataLength()); + } + + return true; +} + +} // namespace sfntly + +TEST(Serialization, Simple) { + ASSERT_TRUE(sfntly::TestSerialization()); +} + +TEST(Serialization, Bitmap) { + ASSERT_TRUE(sfntly::TestSerializationBitmap()); +} diff --git a/gfx/sfntly/cpp/src/test/serialization_test.h b/gfx/sfntly/cpp/src/test/serialization_test.h new file mode 100644 index 0000000000..8996793585 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/serialization_test.h @@ -0,0 +1,34 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SFNTLY_CPP_SRC_TEST_SERIALIZATION_TEST_H_ +#define SFNTLY_CPP_SRC_TEST_SERIALIZATION_TEST_H_ + +#include "sfntly/table/table.h" + +namespace sfntly { + +bool VerifyHHEA(Table* original, Table* target); +bool VerifyGLYF(Table* original, Table* target); +bool VerifyHMTX(Table* original, Table* target); +bool VerifyLOCA(Table* original, Table* target); +bool VerifyMAXP(Table* original, Table* target); +bool VerifyNAME(Table* original, Table* target); +bool VerifyOS_2(Table* original, Table* target); + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_SERIALIZATION_TEST_H_ diff --git a/gfx/sfntly/cpp/src/test/smart_pointer_test.cc b/gfx/sfntly/cpp/src/test/smart_pointer_test.cc new file mode 100644 index 0000000000..9e81baaba6 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/smart_pointer_test.cc @@ -0,0 +1,79 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#define ENABLE_OBJECT_COUNTER +#include "sfntly/port/refcount.h" + +using sfntly::RefCounted; +using sfntly::Ptr; + +class Foo : public RefCounted<Foo> { +public: // put in something to make sure it's not empty + int foo_; + int foo() { return foo_; } +}; + +bool TestSmartPointer() { + // scope out allocation + { + Ptr<Foo> p1; + p1 = new Foo(); + EXPECT_EQ(size_t(1), p1->ref_count_); + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + + Ptr<Foo> p2; + p2 = p1; + EXPECT_EQ(size_t(2), p1->ref_count_); + EXPECT_EQ(size_t(2), p2->ref_count_); + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + + Ptr<Foo> p3; + p3 = p1; + EXPECT_EQ(size_t(3), p1->ref_count_); + EXPECT_EQ(size_t(3), p2->ref_count_); + EXPECT_EQ(size_t(3), p3->ref_count_); + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + + p2 = new Foo(); + EXPECT_EQ(size_t(2), p1->ref_count_); + EXPECT_EQ(size_t(1), p2->ref_count_); + EXPECT_EQ(size_t(2), p3->ref_count_); + EXPECT_EQ(size_t(2), RefCounted<Foo>::object_counter_); + + p3.Release(); + EXPECT_EQ(size_t(1), p1->ref_count_); + EXPECT_EQ(NULL, p3.p_); + EXPECT_EQ(size_t(2), RefCounted<Foo>::object_counter_); + + p2 = NULL; + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + + p1 = p1; + EXPECT_EQ(size_t(1), p1->ref_count_); + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + + p1 = &(*p1); + EXPECT_EQ(size_t(1), p1->ref_count_); + EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_); + } + EXPECT_EQ(size_t(0), RefCounted<Foo>::object_counter_); + return true; +} + +TEST(SmartPointer, All) { + ASSERT_TRUE(TestSmartPointer()); +} diff --git a/gfx/sfntly/cpp/src/test/test_data.cc b/gfx/sfntly/cpp/src/test/test_data.cc new file mode 100644 index 0000000000..05bc7595e0 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_data.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sfntly/tag.cc" +#include "test/test_data.h" + +namespace sfntly { + +// If the TTF file used in test changed, the verify*.cc in test need to be +// changed also. +// TODO(arthurhsu): Refactor this into a test class and have all const inside. +// This way we can test multiple fonts using same set of +// code. + +const char* SAMPLE_TTF_FILE = "Tuffy.ttf"; +const char* SAMPLE_BITMAP_FONT = "AnonymousPro-Regular.ttf"; + +const size_t SAMPLE_TTF_SIZE = 183936; +const size_t SAMPLE_TTF_TABLES = 17; +const size_t SAMPLE_TTF_KNOWN_TAGS = 16; +const size_t SAMPLE_BITMAP_KNOWN_TAGS = 20; +const size_t SAMPLE_TTF_FEAT = 3; +const size_t SAMPLE_TTF_HEAD = 6; +const size_t SAMPLE_TTF_POST = 14; + +const int32_t TTF_KNOWN_TAGS[] = { + Tag::OS_2, Tag::cmap, Tag::cvt, Tag::feat, Tag::gasp, + Tag::glyf, Tag::head, Tag::hhea, Tag::hmtx, Tag::kern, + Tag::loca, Tag::maxp, Tag::morx, Tag::name, Tag::post, + Tag::prop }; + +const int32_t BITMAP_KNOWN_TAGS[] = { + Tag::EBDT, Tag::EBLC, Tag::EBSC, Tag::LTSH, Tag::OS_2, + Tag::VDMX, Tag::cmap, Tag::cvt, Tag::fpgm, Tag::gasp, + Tag::glyf, Tag::hdmx, Tag::head, Tag::hhea, Tag::hmtx, + Tag::loca, Tag::maxp, Tag::name, Tag::post, Tag::prep }; + +const int64_t TTF_CHECKSUM[] = { + 0xD463FC48, 0x252028D1, 0x0065078A, 0xC01407B5, 0xFFFF0003, + 0x9544342B, 0xFC8F16AD, 0x0EC30C7A, 0xA029CD5D, 0x32513087, + 0x05C323B0, 0x06320195, 0x3B67E701, 0xE7DB08F3, 0xD46E5E89, + 0xE6EB4A27 }; + +const int64_t TTF_OFFSET[] = { + 0x00000198, 0x00001964, 0x000025B0, 0x0002CA74, 0x0002C854, + 0x00003D34, 0x0000011C, 0x00000154, 0x000001F0, 0x000245D8, + 0x000025B8, 0x00000178, 0x0002CAB4, 0x00024860, 0x00028854, + 0x0002C85C }; + +const int32_t TTF_LENGTH[] = { + 86, 3146, 8, 64, 8, + 133284, 54, 36, 6002, 648, + 6012, 32, 944, 16371, 16383, + 536 }; + +const unsigned char TTF_FEAT_DATA[] = { + 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0x30, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0x34, + 0, 0, 1, 1, 0, 0xB, 0, 2, 0, 0, 0, 0x38, 0xC0, 0, 1, 2, + 0, 0, 1, 3, 0, 2, 1, 4, 0, 0, 1, 5, 0, 2, 1, 6 }; + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/test_data.h b/gfx/sfntly/cpp/src/test/test_data.h new file mode 100644 index 0000000000..d5e576fa0d --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_data.h @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SFNTLY_CPP_SRC_TEST_TEST_DATA_H_ +#define SFNTLY_CPP_SRC_TEST_TEST_DATA_H_ + +#include "sfntly/tag.h" + +namespace sfntly { + +extern const char* SAMPLE_TTF_FILE; +extern const char* SAMPLE_BITMAP_FONT; + +extern const size_t SAMPLE_TTF_SIZE; +extern const size_t SAMPLE_TTF_TABLES; +extern const size_t SAMPLE_TTF_KNOWN_TAGS; +extern const size_t SAMPLE_BITMAP_KNOWN_TAGS; +extern const size_t SAMPLE_TTF_FEAT; +extern const size_t SAMPLE_TTF_HEAD; +extern const size_t SAMPLE_TTF_POST; + +extern const int32_t TTF_KNOWN_TAGS[]; +extern const int32_t BITMAP_KNOWN_TAGS[]; +extern const int64_t TTF_CHECKSUM[]; +extern const int64_t TTF_OFFSET[]; +extern const int32_t TTF_LENGTH[]; +extern const unsigned char TTF_FEAT_DATA[]; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_TEST_DATA_H_ diff --git a/gfx/sfntly/cpp/src/test/test_font_utils.cc b/gfx/sfntly/cpp/src/test/test_font_utils.cc new file mode 100644 index 0000000000..a00a811d6c --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_font_utils.cc @@ -0,0 +1,115 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "gtest/gtest.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/data/growable_memory_byte_array.h" +#include "sfntly/port/file_input_stream.h" +#include "test/test_font_utils.h" + +namespace sfntly { + +void BuilderForFontFile(const char* font_path, FontFactory* factory, + FontBuilderArray* builders) { + assert(factory); + FileInputStream is; + is.Open(font_path); + factory->LoadFontsForBuilding(&is, builders); + EXPECT_GT(builders->size(), static_cast<size_t>(0)); +} + +void SerializeFont(const char* font_path, FontFactory* factory, Font* font) { + assert(font_path); + assert(factory); + assert(font); + MemoryOutputStream output_stream; + factory->SerializeFont(font, &output_stream); + SerializeToFile(&output_stream, font_path); +} + +void LoadFont(const char* font_path, FontFactory* factory, FontArray* fonts) { + FileInputStream is; + is.Open(font_path); + factory->LoadFonts(&is, fonts); + is.Close(); +} + +void LoadFontUsingByteVector(const char* font_path, + bool fingerprint, + FontArray* fonts) { + ByteVector bv; + LoadFile(font_path, &bv); + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + factory->FingerprintFont(fingerprint); + factory->LoadFonts(&bv, fonts); +} + +void LoadFile(const char* input_file_path, ByteVector* input_buffer) { + assert(input_file_path); + assert(input_buffer); + + FILE* input_file = NULL; +#if defined WIN32 + fopen_s(&input_file, input_file_path, "rb"); +#else + input_file = fopen(input_file_path, "rb"); +#endif + EXPECT_NE(input_file, static_cast<FILE*>(NULL)); + fseek(input_file, 0, SEEK_END); + size_t file_size = ftell(input_file); + fseek(input_file, 0, SEEK_SET); + input_buffer->resize(file_size); + size_t bytes_read = fread(&((*input_buffer)[0]), 1, file_size, input_file); + EXPECT_EQ(bytes_read, file_size); + fclose(input_file); +} + +void SerializeToFile(MemoryOutputStream* output_stream, const char* file_path) { + assert(file_path); + assert(output_stream); + + FILE* output_file = NULL; +#if defined WIN32 + fopen_s(&output_file, file_path, "wb"); +#else + output_file = fopen(file_path, "wb"); +#endif + EXPECT_NE(output_file, static_cast<FILE*>(NULL)); + fwrite(output_stream->Get(), 1, output_stream->Size(), output_file); + fflush(output_file); + fclose(output_file); +} + +void HexDump(const unsigned char* byte_data, size_t length) { + if (byte_data == NULL || length == 0) { + fprintf(stderr, "<NULL>\n"); + return; + } + + fprintf(stderr, "data length = %ld (%lx)\n", length, length); + for (size_t i = 0; i < length; ++i) { + fprintf(stderr, "%02x ", byte_data[i]); + if ((i & 0xf) == 0xf) { + fprintf(stderr, "\n"); + } + } + fprintf(stderr, "\n"); +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/test_font_utils.h b/gfx/sfntly/cpp/src/test/test_font_utils.h new file mode 100644 index 0000000000..57fde7a658 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_font_utils.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_ +#define SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_ + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/port/memory_output_stream.h" + +namespace sfntly { + +void BuilderForFontFile(const char* font_path, FontFactory* factory, + FontBuilderArray* builders); +void SerializeFont(const char* font_path, FontFactory* factory, Font* font); +void LoadFont(const char* font_path, FontFactory* factory, FontArray* fonts); +void LoadFontUsingByteVector(const char* font_path, + bool fingerprint, + FontArray* fonts); + +void LoadFile(const char* input_file_path, ByteVector* input_buffer); +void SerializeToFile(MemoryOutputStream* output_stream, const char* file_path); + +void HexDump(const unsigned char* byte_data, size_t length); + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_ diff --git a/gfx/sfntly/cpp/src/test/test_utils.cc b/gfx/sfntly/cpp/src/test/test_utils.cc new file mode 100644 index 0000000000..4751b92625 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_utils.cc @@ -0,0 +1,89 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test/test_utils.h" + +#include <stdio.h> +#include <unicode/ucnv.h> +#include <unicode/uchar.h> + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/data/growable_memory_byte_array.h" +#include "sfntly/port/file_input_stream.h" + +namespace sfntly { +TestUtils::TestUtils() {} + +// static +// OutputStream CreateOutputStream(const char *file_path) { +// } + +// static +// void TestUtils::CreateNewFile(const char* file_path) { +// } + +// static +int32_t TestUtils::EncodeOneChar(UConverter* encoder, int16_t uchar) { + char* target = new char[ucnv_getMaxCharSize(encoder) * 2]; + char* target_end; + UChar* source = new UChar[2]; + UChar* source_end; + source[0] = (UChar)uchar; + source[1] = 0; + UErrorCode status = U_ZERO_ERROR; + source_end = source; + target_end = target; + ucnv_fromUnicode(encoder, &target_end, target + 4, + (const UChar**)&source_end, source + sizeof(UChar), + NULL, TRUE, &status); + if (!U_SUCCESS(status)) { + fprintf(stderr, "Error occured in conversion of %d: %s\n", + uchar, u_errorName(status)); + delete[] source; + delete[] target; + return 0; + } + int32_t enc_char = 0; + for (int32_t position = 0; position < target_end - target; ++position) { + enc_char <<= 8; + enc_char |= (target[position] & 0xff); + } + delete[] source; + delete[] target; + return enc_char; +} + +// static +UConverter* TestUtils::GetEncoder(const char* charset_name) { + if (charset_name == NULL || strcmp(charset_name, "") == 0) + return NULL; + UErrorCode status = U_ZERO_ERROR; + UConverter* conv = ucnv_open(charset_name, &status); + // if (!U_SUCCESS(status)) + // return NULL; + return conv; // returns NULL @ error anyway +} + +// Get a file's extension +// static +const char* TestUtils::Extension(const char* file_path) { + if (!file_path) + return NULL; + return strrchr(file_path, EXTENSION_SEPARATOR); +} +} diff --git a/gfx/sfntly/cpp/src/test/test_utils.h b/gfx/sfntly/cpp/src/test/test_utils.h new file mode 100644 index 0000000000..af3ffd6c83 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_utils.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SFNTLY_CPP_SRC_TEST_TEST_UTILS_H_ +#define SFNTLY_CPP_SRC_TEST_TEST_UTILS_H_ + +// Must include this before ICU to avoid stdint redefinition issue. +#include "sfntly/port/type.h" + +#include <unicode/ucnv.h> + +#include <string> + +#include "sfntly/font.h" +#include "sfntly/data/memory_byte_array.h" + +namespace sfntly { +class TestUtils { + TestUtils(); + + public: + // Compare sections of two byte arrays for equality + // @param b1 byte array 1 + // @param offset1 offset for comparison in byte array 1 + // @param b2 byte array 2 + // @param offset2 offset for comparison in byte array 2 + // @param length the length of the byte arrays to compare + // @return true if the array segments are equal; false otherwise + // TODO(dfilimon): implement + static bool Equals(ByteArray* b1, + int32_t offset1, + ByteArray* b2, + int32_t offset2); + + // @param offset1 offset to start comparing the first ByteArray from + // @param ba1 the first ByteArray + // @param offset2 offset to start comparing the second ByteArray from + // @param ba2 the second ByteArray + // @param length the number of bytes to compare + // @return true if all bytes in the ranges given are equal; false otherwise + // TODO(dfilimon): implement + static bool Equals(ByteArray* b1, + int32_t offset1, + ByteArray* b2, + int32_t offset2, + int32_t length); + + // TODO(dfilimon): implement FileOutputStream in port/file_output_stream.* + // static OutputStream createOutputStream(const char* file_path); + + // TODO(dfilimon): adapt & implement + // static FileChannel createFilechannelForWriting(File file); + + // Creates a new file including deleting an already existing file with the + // same path and name and creating any needed directories. + // TODO(dfilimon): implement + static void CreateNewFile(const char* file_path); + + // Converts an integer into a 4 character string using the ASCII encoding. + // @param i the value to convert + // @return the String based on the number + // TODO(dfilimon): implement + static void DumpLongAsString(int32_t i, std::string* result); + + // Calculate an OpenType checksum from the array. + // @param b the array to calculate checksum on + // @param offset the starting index in the array + // @param length the number of bytes to check; must be a multiple of 4 + // @return checksum + // TODO(dfilimon): implement + static int64_t CheckSum(ByteArray* b, int32_t offset, int32_t length); + + // Encode a single character in UTF-16. + // We only support the BMP for now + // @param encoder the encoder to use for the encoding + // @param uchar the Unicode character to encode + // @return the encoded character + static int32_t EncodeOneChar(UConverter* encoder, int16_t uchar); + + // Get an encoder for the charset name. + // If the name is null or the empty string then just return null. + // @param charsetName the charset to get an encoder for + // @return an encoder or null if no encoder available for charset name + static UConverter* GetEncoder(const char* charsetName); + + private: + static const char EXTENSION_SEPARATOR = '.'; + + public: + // Get the extension of a file's name. + // @param file the whose name to process + // @return string containing the extension or an empty string if + // there is no extension + static const char* Extension(const char* file_path); +}; +} +#endif // SFNTLY_CPP_SRC_TEST_TEST_UTILS_H_ diff --git a/gfx/sfntly/cpp/src/test/test_utils_test.cc b/gfx/sfntly/cpp/src/test/test_utils_test.cc new file mode 100644 index 0000000000..fbd5ed1a28 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_utils_test.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Must include at the first line to avoid ICU / stdint conflict. +#include "sfntly/port/type.h" + +#include <stdio.h> +#include <unicode/ucnv.h> +#include <unicode/uchar.h> + +#include "gtest/gtest.h" +#include "test/test_utils.h" + +namespace sfntly { + +// Check if proper encoding is being performed +// Conversion is done from UTF16 to UTF8, SJIS +bool TestEncoding() { + UConverter* conv = TestUtils::GetEncoder("utf8"); + EXPECT_TRUE(conv != NULL); + // Ūnĭcōde̽ + UChar from[8] = {0x016A, 0x006E, 0x012D, 0x0063, 0x014D, 0x0064, 0x0065, + 0x033D}; + int32_t want[12] = {0xc5, 0xaa, 0x6e, 0xc4, 0xad, 0x63, 0xc5, 0x8d, 0x64, + 0x65, 0xcc, 0xbd}; + int32_t i, j = 0; + for (i = 0; i < 7; ++i) { + int32_t encoded = TestUtils::EncodeOneChar(conv, (int16_t)from[i]); + for (; encoded; encoded <<= 8) { + uint8_t b = (encoded & 0xff000000) >> 24; + if (!b) + continue; + EXPECT_EQ(want[j], b); + if (want[j++] != b) { + ucnv_close(conv); + return false; + } + } + } + ucnv_close(conv); + return true; +} + +// Check if the proper extension is obtained +bool TestExtension() { + // usual file name + const char *result; + result = TestUtils::Extension("../data/ext/tuffy.ttf"); + EXPECT_EQ(strcmp(result, ".ttf"), 0); + + // more than one 'extension' + result = TestUtils::Extension("tuffy.ttf.fake"); + EXPECT_EQ(strcmp(result, ".fake"), 0); + + // no extension + result = TestUtils::Extension("tuffy"); + EXPECT_STREQ(result, NULL); + + // bogus extension + result = TestUtils::Extension("tuffy."); + EXPECT_EQ(strcmp(result, "."), 0); + + return true; +} + +} // namespace sfntly + +TEST(TestUtils, All) { + ASSERT_TRUE(sfntly::TestExtension()); + ASSERT_TRUE(sfntly::TestEncoding()); +} diff --git a/gfx/sfntly/cpp/src/test/test_xml_utils.cc b/gfx/sfntly/cpp/src/test/test_xml_utils.cc new file mode 100644 index 0000000000..dc65add747 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_xml_utils.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <map> +#include <string> +#include "test/test_xml_utils.h" +#include "test/tinyxml/tinyxml.h" + +namespace sfntly { +void InternalGetNodesWithName(const TiXmlNode* node, const std::string& name, + TiXmlNodeVector* wanted_nodes) { + if (node->ValueStr() == name) + wanted_nodes->push_back(node); + for (const TiXmlNode* child = node->FirstChild(); + child != NULL; child = child->NextSibling()) { + InternalGetNodesWithName(child, name, wanted_nodes); + } +} + +TiXmlNodeVector* GetNodesWithName(const TiXmlNode* node, + const std::string& name) { + TiXmlNodeVector* wanted_nodes = new TiXmlNodeVector; + InternalGetNodesWithName(node, name, wanted_nodes); + return wanted_nodes; +} + +const TiXmlAttribute* GetAttribute(const TiXmlNode* node, + const std::string& name) { + for (const TiXmlAttribute* attribute = node->ToElement()->FirstAttribute(); + attribute != NULL; attribute = attribute->Next()) { + if (attribute->Name() == name) { + return attribute; + } + } + return NULL; +} +} diff --git a/gfx/sfntly/cpp/src/test/test_xml_utils.h b/gfx/sfntly/cpp/src/test/test_xml_utils.h new file mode 100644 index 0000000000..7a5fe49d6b --- /dev/null +++ b/gfx/sfntly/cpp/src/test/test_xml_utils.h @@ -0,0 +1,33 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sfntly/port/refcount.h" +#include "test/tinyxml/tinyxml.h" + +#ifndef SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_ +#define SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_ + +namespace sfntly { +typedef std::map<std::string, std::string> AttributeMap; +typedef std::vector<const TiXmlNode*> TiXmlNodeVector; + +TiXmlNodeVector* GetNodesWithName(const TiXmlNode* node, + const std::string& name); +const TiXmlAttribute* GetAttribute(const TiXmlNode* node, + const std::string& name); +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_ diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinystr.cpp b/gfx/sfntly/cpp/src/test/tinyxml/tinystr.cpp new file mode 100644 index 0000000000..0665768205 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinystr.h b/gfx/sfntly/cpp/src/test/tinyxml/tinystr.h new file mode 100644 index 0000000000..89cca33415 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include <assert.h> +#include <string.h> + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast<size_type>( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast<size_type>( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast<int*>( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.cpp b/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.cpp new file mode 100644 index 0000000000..9c161dfcb9 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> + +#ifdef TIXML_USE_STL +#include <sstream> +#include <iostream> +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "</%s>", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "<?xml " ); + if ( str ) (*str) += "<?xml "; + + if ( !version.empty() ) { + if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } + } + if ( !encoding.empty() ) { + if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } + } + if ( !standalone.empty() ) { + if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } + } + if ( cfile ) fprintf( cfile, "?>" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += "</"; + buffer += element.Value(); + buffer += ">"; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += "<![CDATA["; + buffer += text.Value(); + buffer += "]]>"; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += "<!--"; + buffer += comment.Value(); + buffer += "-->"; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.h b/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.h new file mode 100644 index 0000000000..a3589e5b26 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinyxml.h @@ -0,0 +1,1805 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include <string> + #include <iostream> + #include <sstream> + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, <b>no children of this node or its sibilings</b> will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + <foo>This is text</foo> + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + <foo><b>This is text</b></foo> + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + <foo>This is <b>text</b></foo> + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i<depth; ++i ) + buffer += indent; + } + void DoLineBreak() { + buffer += lineBreak; + } + + int depth; + bool simpleTextPrint; + TIXML_STRING buffer; + TIXML_STRING indent; + TIXML_STRING lineBreak; +}; + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlerror.cpp b/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlerror.cpp new file mode 100644 index 0000000000..538c21d0bd --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,52 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", + "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", +}; diff --git a/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlparser.cpp b/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlparser.cpp new file mode 100644 index 0000000000..81b7eae96b --- /dev/null +++ b/gfx/sfntly/cpp/src/test/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1638 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> +#include <stddef.h> + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include <windows.h> +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + //*length = 1; // Leave unrecognized entities - this doesn't really work. + // Just writes strange XML. + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = { "<?xml" }; + const char* commentHeader = { "<!--" }; + const char* dtdHeader = { "<!" }; + const char* cdataHeader = { "<![CDATA[" }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); + #endif + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); + #endif + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, cdataHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing CDATA\n" ); + #endif + TiXmlText* text = new TiXmlText( "" ); + text->SetCDATA( true ); + returnNode = text; + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); + #endif + returnNode = new TiXmlElement( "" ); + } + else + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // cdata text (which looks like another node) + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) return; + assert( in->peek() == '<' ); + int tagIndex = (int) tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get(); + + // Early out if we find the CDATA id. + if ( c == '[' && tag->size() >= 9 ) + { + size_t len = tag->size(); + const char* start = tag->c_str() + len - 9; + if ( strcmp( start, "<![CDATA[" ) == 0 ) { + assert( !closingTag ); + break; + } + } + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) { + // We were looking for the end tag, but found nothing. + // Fix for [ 1663758 ] Failure to report error on bad XML + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + + // We should find the end tag now + // note that: + // </foo > and + // </foo> + // are both valid end tags. + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + p = SkipWhiteSpace( p, encoding ); + if ( p && *p && *p == '>' ) { + ++p; + return p; + } + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + else + { + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + return 0; + } + + attrib->SetDocument( document ); + pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + #ifdef TIXML_USE_STL + TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); + #else + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + #endif + if ( node ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + + // Read in text and elements in any order. + const char* pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + LinkEndChild( textNode ); + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? This could also be + // a TiXmlText in the "CDATA" style. + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( p && *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + <!-- declarations for <head> & <body> --> + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = "<![CDATA["; + const char* const endTag = "]]>"; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} + diff --git a/gfx/sfntly/cpp/src/test/verify_glyf.cc b/gfx/sfntly/cpp/src/test/verify_glyf.cc new file mode 100644 index 0000000000..abfe1ab8aa --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_glyf.cc @@ -0,0 +1,59 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +// We spot check only glyph id 33. +const int32_t GLYPH33_OFFSET = 0xAC8; +const int32_t GLYPH33_LENGTH = 40; +const int32_t GLYPH33_XMIN = 92; +const int32_t GLYPH33_YMIN = 20; +const int32_t GLYPH33_XMAX = 797; +const int32_t GLYPH33_YMAX = 1235; + +// TODO(arthurhsu): Tuffy does not have composite glyphs. Need better testing. +static bool VerifyGLYF(Table* table) { + GlyphTablePtr glyf_table = down_cast<GlyphTable*>(table); + if (glyf_table == NULL) { + return false; + } + + GlyphPtr glyf; + glyf.Attach(glyf_table->GetGlyph(GLYPH33_OFFSET, GLYPH33_LENGTH)); + if (glyf == NULL) { + return false; + } + + EXPECT_EQ(glyf->XMin(), GLYPH33_XMIN); + EXPECT_EQ(glyf->YMin(), GLYPH33_YMIN); + EXPECT_EQ(glyf->XMax(), GLYPH33_XMAX); + EXPECT_EQ(glyf->YMax(), GLYPH33_YMAX); + + return true; +} + +bool VerifyGLYF(Table* original, Table* target) { + EXPECT_TRUE(VerifyGLYF(original)); + EXPECT_TRUE(VerifyGLYF(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_hhea.cc b/gfx/sfntly/cpp/src/test/verify_hhea.cc new file mode 100644 index 0000000000..1e1001cd66 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_hhea.cc @@ -0,0 +1,63 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/math/fixed1616.h" +#include "sfntly/table/core/horizontal_header_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t HHEA_ASCENDER = 2023; +const int32_t HHEA_DESCENDER = -648; +// const int32_t HHEA_LINE_GAP = 93; +const int32_t HHEA_ADVANCE_WIDTH_MAX = 2753; +const int32_t HHEA_MIN_LSB = -968; +const int32_t HHEA_MIN_RSB = -411; +const int32_t HHEA_X_MAX_EXTENT = 2628; +const int32_t HHEA_METRIC_DATA_FORMAT = 0; +const int32_t HHEA_NUM_METRICS = 1499; + +static bool VerifyHHEA(Table* table) { + HorizontalHeaderTablePtr hhea = down_cast<HorizontalHeaderTable*>(table); + if (hhea == NULL) { + return false; + } + + EXPECT_EQ(hhea->TableVersion(), Fixed1616::Fixed(1, 0)); + EXPECT_EQ(hhea->Ascender(), HHEA_ASCENDER); + EXPECT_EQ(hhea->Descender(), HHEA_DESCENDER); + EXPECT_EQ(hhea->AdvanceWidthMax(), HHEA_ADVANCE_WIDTH_MAX); + EXPECT_EQ(hhea->MinLeftSideBearing(), HHEA_MIN_LSB); + EXPECT_EQ(hhea->MinRightSideBearing(), HHEA_MIN_RSB); + EXPECT_EQ(hhea->XMaxExtent(), HHEA_X_MAX_EXTENT); + // TODO(arthurhsu): CaretSlopeRise() not tested + // TODO(arthurhsu): CaretSlopeRun() not tested + // TODO(arthurhsu): CaretOffset() not tested + EXPECT_EQ(hhea->MetricDataFormat(), HHEA_METRIC_DATA_FORMAT); + EXPECT_EQ(hhea->NumberOfHMetrics(), HHEA_NUM_METRICS); + + return true; +} + +bool VerifyHHEA(Table* original, Table* target) { + EXPECT_TRUE(VerifyHHEA(original)); + EXPECT_TRUE(VerifyHHEA(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_hmtx.cc b/gfx/sfntly/cpp/src/test/verify_hmtx.cc new file mode 100644 index 0000000000..d37bba98b2 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_hmtx.cc @@ -0,0 +1,76 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/core/horizontal_metrics_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t HMTX_ENTRIES_COUNT = 1499; +const int32_t HMTX_LSB_COUNT = 3; + +struct HmtxEntry { + int32_t advance_width_; + int32_t lsb_; + + HmtxEntry(int32_t advance_width, int32_t lsb) + : advance_width_(advance_width), lsb_(lsb) {} +}; + +const HmtxEntry HMTX_ENTRIES[] = { + HmtxEntry(748, 68), // 0 + HmtxEntry(0, 0), // 1 + HmtxEntry(682, 0), // 2 + HmtxEntry(616, 0), // 3 + HmtxEntry(421, 103), // 4 + HmtxEntry(690, 129), // 5 + HmtxEntry(1589, 129), // 6 + HmtxEntry(1017, 25), // 7 + HmtxEntry(1402, 104), // 8 + HmtxEntry(1241, 100), // 9 +}; +const int32_t NUM_HMTX_ENTRIES = 10; + +static bool VerifyHMTX(Table* table) { + HorizontalMetricsTablePtr hmtx = down_cast<HorizontalMetricsTable*>(table); + if (hmtx == NULL) { + return false; + } + + EXPECT_EQ(hmtx->NumberOfHMetrics(), HMTX_ENTRIES_COUNT); + EXPECT_EQ(hmtx->NumberOfLSBs(), HMTX_LSB_COUNT); + + for (int32_t i = 0; i < NUM_HMTX_ENTRIES; ++i) { + EXPECT_EQ(hmtx->AdvanceWidth(i), HMTX_ENTRIES[i].advance_width_); + EXPECT_EQ(hmtx->LeftSideBearing(i), HMTX_ENTRIES[i].lsb_); + } + + // No such element case. + EXPECT_EQ(hmtx->AdvanceWidth(HMTX_ENTRIES_COUNT), + HMTX_ENTRIES[0].advance_width_); + EXPECT_EQ(hmtx->LeftSideBearing(HMTX_ENTRIES_COUNT), HMTX_ENTRIES[0].lsb_); + return true; +} + +bool VerifyHMTX(Table* original, Table* target) { + EXPECT_TRUE(VerifyHMTX(original)); + EXPECT_TRUE(VerifyHMTX(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_loca.cc b/gfx/sfntly/cpp/src/test/verify_loca.cc new file mode 100644 index 0000000000..4a32928875 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_loca.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/truetype/loca_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t LOCA_NUM_LOCAS = 1503; +const int32_t LOCAS[] = { + 0x00000, // 0 + 0x00058, // 1 + 0x00058, // 2 + 0x00058, // 3 + 0x00058, // 4 + 0x000B8, // 5 + 0x00138, // 6 + 0x001A4, // 7 + 0x0025C, // 8 + 0x00328, // 9 + 0x003B8, // 10 +}; +const int32_t NUM_TEST_LOCAS = 11; + +static bool VerifyLOCA(Table* table) { + LocaTablePtr loca = down_cast<LocaTable*>(table); + if (loca == NULL) { + return false; + } + + EXPECT_EQ(loca->NumLocas(), LOCA_NUM_LOCAS); + EXPECT_EQ(loca->num_glyphs(), LOCA_NUM_LOCAS - 1); + + for (int32_t i = 0; i < NUM_TEST_LOCAS - 1; ++i) { + EXPECT_EQ(loca->GlyphOffset(i), LOCAS[i]); + EXPECT_EQ(loca->GlyphLength(i), LOCAS[i + 1] - LOCAS[i]); + } + return true; +} + +bool VerifyLOCA(Table* original, Table* target) { + EXPECT_TRUE(VerifyLOCA(original)); + EXPECT_TRUE(VerifyLOCA(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_maxp.cc b/gfx/sfntly/cpp/src/test/verify_maxp.cc new file mode 100644 index 0000000000..074042a0b9 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_maxp.cc @@ -0,0 +1,72 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/math/fixed1616.h" +#include "sfntly/table/core/maximum_profile_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t MAXP_NUM_GLYPHS = 1502; +const int32_t MAXP_MAX_POINTS = 181; +const int32_t MAXP_MAX_CONTOURS = 9; +const int32_t MAXP_MAX_COMPOSITE_POINTS = 172; +const int32_t MAXP_MAX_COMPOSITE_CONTOURS = 5; +const int32_t MAXP_MAX_ZONES = 2; +const int32_t MAXP_MAX_TWILIGHT_POINTS = 0; +const int32_t MAXP_MAX_STORAGE = 1; +const int32_t MAXP_MAX_FUNCTION_DEFS = 1; +// const int32_t MAXP_MAX_INSTR_DEFS = 0; +const int32_t MAXP_MAX_STACK_ELEMENTS = 64; +const int32_t MAXP_MAX_INSTR_SIZE = 46; +const int32_t MAXP_MAX_COMPONENT_ELEMENTS = 4; +const int32_t MAXP_MAX_COMPONENT_DEPTH = 3; + +static bool VerifyMAXP(Table* table) { + MaximumProfileTablePtr maxp = down_cast<MaximumProfileTable*>(table); + if (maxp == NULL) { + return false; + } + + EXPECT_EQ(maxp->TableVersion(), Fixed1616::Fixed(1, 0)); + EXPECT_EQ(maxp->NumGlyphs(), MAXP_NUM_GLYPHS); + EXPECT_EQ(maxp->MaxPoints(), MAXP_MAX_POINTS); + EXPECT_EQ(maxp->MaxContours(), MAXP_MAX_CONTOURS); + EXPECT_EQ(maxp->MaxCompositePoints(), MAXP_MAX_COMPOSITE_POINTS); + EXPECT_EQ(maxp->MaxCompositeContours(), MAXP_MAX_COMPOSITE_CONTOURS); + EXPECT_EQ(maxp->MaxZones(), MAXP_MAX_ZONES); + EXPECT_EQ(maxp->MaxTwilightPoints(), MAXP_MAX_TWILIGHT_POINTS); + EXPECT_EQ(maxp->MaxStorage(), MAXP_MAX_STORAGE); + EXPECT_EQ(maxp->MaxFunctionDefs(), MAXP_MAX_FUNCTION_DEFS); + // TODO(arthurhsu): maxInstructionDefs observed in Microsoft TTF report. + // Check with stuartg and see if this is a miss. + EXPECT_EQ(maxp->MaxStackElements(), MAXP_MAX_STACK_ELEMENTS); + EXPECT_EQ(maxp->MaxSizeOfInstructions(), MAXP_MAX_INSTR_SIZE); + EXPECT_EQ(maxp->MaxComponentElements(), MAXP_MAX_COMPONENT_ELEMENTS); + EXPECT_EQ(maxp->MaxComponentDepth(), MAXP_MAX_COMPONENT_DEPTH); + + return true; +} + +bool VerifyMAXP(Table* original, Table* target) { + EXPECT_TRUE(VerifyMAXP(original)); + EXPECT_TRUE(VerifyMAXP(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_name.cc b/gfx/sfntly/cpp/src/test/verify_name.cc new file mode 100644 index 0000000000..e1101c078f --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_name.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/core/name_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t NAME_FORMAT = 0; +const int32_t NAME_COUNT = 75; +const NameTable::NameEntryId NAME_IDS[] = { + NameTable::NameEntryId(1, 0, 0, 0), // 0 + NameTable::NameEntryId(1, 0, 0, 1), // 1 + NameTable::NameEntryId(1, 0, 0, 2), // 2 + NameTable::NameEntryId(1, 0, 0, 3), // 3 + NameTable::NameEntryId(1, 0, 0, 4), // 4 + NameTable::NameEntryId(1, 0, 0, 5), // 5 + NameTable::NameEntryId(1, 0, 0, 6), // 6 + NameTable::NameEntryId(1, 0, 0, 9), // 7 + NameTable::NameEntryId(1, 0, 0, 11), // 8 + NameTable::NameEntryId(1, 0, 0, 12), // 9 +}; +const int32_t NAME_IDS_TEST = 10; + +static bool VerifyNAME(Table* table) { + // TODO(arthurhsu): Better testing can be done here. Right now we just + // iterate through the entries and get entry ids. + NameTablePtr name = down_cast<NameTable*>(table); + if (name == NULL) { + return false; + } + + EXPECT_EQ(name->Format(), NAME_FORMAT); + EXPECT_EQ(name->NameCount(), NAME_COUNT); + fprintf(stderr, "checking name entry: "); + for (int32_t i = 0; i < NAME_IDS_TEST; ++i) { + fprintf(stderr, "%d ", i); + EXPECT_EQ(name->PlatformId(i), NAME_IDS[i].platform_id()); + EXPECT_EQ(name->EncodingId(i), NAME_IDS[i].encoding_id()); + EXPECT_EQ(name->LanguageId(i), NAME_IDS[i].language_id()); + EXPECT_EQ(name->NameId(i), NAME_IDS[i].name_id()); + } + fprintf(stderr, "\n"); + return true; +} + +bool VerifyNAME(Table* original, Table* target) { + EXPECT_TRUE(VerifyNAME(original)); + EXPECT_TRUE(VerifyNAME(target)); + return true; +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/test/verify_os2.cc b/gfx/sfntly/cpp/src/test/verify_os2.cc new file mode 100644 index 0000000000..c80c58a8f5 --- /dev/null +++ b/gfx/sfntly/cpp/src/test/verify_os2.cc @@ -0,0 +1,125 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" +#include "sfntly/font.h" +#include "sfntly/table/core/os2_table.h" +#include "test/serialization_test.h" + +namespace sfntly { + +const int32_t OS2_VERSION = 1; +const int32_t OS2_XAVG_CHAR_WIDTH = 863; +const int32_t OS2_US_WEIGHT_CLASS = 500; +const int32_t OS2_US_WIDTH_CLASS = 5; +const int32_t OS2_FS_TYPE = 0; +const int32_t OS2_YSUBS_XSIZE = 0; +const int32_t OS2_YSUBS_YSIZE = 2; +const int32_t OS2_YSUBS_XOFFSET = -16560; +const int32_t OS2_YSUBS_YOFFSET = 0; +const int32_t OS2_YSUPS_XSIZE = -25944; +const int32_t OS2_YSUPS_YSIZE = -27176; +const int32_t OS2_YSUPS_XOFFSET = -16376; +const int32_t OS2_YSUPS_YOFFSET = 1; +const int32_t OS2_YSTRIKEOUT_SIZE = 12312; +const int32_t OS2_YSTRIKEOUT_POS = -16224; +const int32_t OS2_SFAMILY_CLASS = 0; +const uint8_t OS2_PANOSE[] = { 2, 11, 6, 3, 6, 1, 0, 0, 0, 0 }; +const int64_t OS2_UL_UNICODE_RANGE1 = 0xE00002FFL; +const int64_t OS2_UL_UNICODE_RANGE2 = 0x520020FBL; +const int64_t OS2_UL_UNICODE_RANGE3 = 0L; +const int64_t OS2_UL_UNICODE_RANGE4 = 0L; +const uint8_t OS2_ACH_VEND_ID[] = { 'P', 'f', 'E', 'd' }; +const int32_t OS2_FS_SELECTION = 0x0040; +const int32_t OS2_US_FIRST_CHAR_IDX = 0x0020; +const int32_t OS2_US_LAST_CHAR_IDX = 0xFFFF; +const int32_t OS2_STYPO_ASCENDER = 1597; +const int32_t OS2_STYPO_DESCENDER = -451; +const int32_t OS2_STYPO_LINE_GAP = 0; +const int32_t OS2_US_WIN_ASCENT = 2023; +const int32_t OS2_US_WIN_DESCENT = 648; +const int64_t OS2_UL_CODE_PAGE_RANGE1 = 0x2000019FL; +const int64_t OS2_UL_CODE_PAGE_RANGE2 = 0x00000000L; + +static bool VerifyOS_2(Table* table) { + OS2TablePtr os2 = down_cast<OS2Table*>(table); + if (os2 == NULL) { + return false; + } + + EXPECT_EQ(os2->TableVersion(), OS2_VERSION); + EXPECT_EQ(os2->XAvgCharWidth(), OS2_XAVG_CHAR_WIDTH); + EXPECT_EQ(os2->UsWeightClass(), OS2_US_WEIGHT_CLASS); + EXPECT_EQ(os2->UsWidthClass(), OS2_US_WIDTH_CLASS); + EXPECT_EQ(os2->FsType(), OS2_FS_TYPE); + EXPECT_EQ(os2->YSubscriptXSize(), OS2_YSUBS_XSIZE); + EXPECT_EQ(os2->YSubscriptYSize(), OS2_YSUBS_YSIZE); + EXPECT_EQ(os2->YSubscriptXOffset(), OS2_YSUBS_XOFFSET); + EXPECT_EQ(os2->YSubscriptYOffset(), OS2_YSUBS_YOFFSET); + EXPECT_EQ(os2->YSuperscriptXSize(), OS2_YSUPS_XSIZE); + EXPECT_EQ(os2->YSuperscriptYSize(), OS2_YSUPS_YSIZE); + EXPECT_EQ(os2->YSuperscriptXOffset(), OS2_YSUPS_XOFFSET); + EXPECT_EQ(os2->YSuperscriptYOffset(), OS2_YSUPS_YOFFSET); + EXPECT_EQ(os2->YStrikeoutSize(), OS2_YSTRIKEOUT_SIZE); + EXPECT_EQ(os2->YStrikeoutPosition(), OS2_YSTRIKEOUT_POS); + EXPECT_EQ(os2->SFamilyClass(), OS2_SFAMILY_CLASS); + + ByteVector panose; + os2->Panose(&panose); + EXPECT_EQ(panose.size(), sizeof(OS2_PANOSE)); + for (size_t i = 0; i < panose.size(); ++i) { + EXPECT_EQ(panose[i], OS2_PANOSE[i]); + } + + EXPECT_EQ(os2->UlUnicodeRange1(), OS2_UL_UNICODE_RANGE1); + EXPECT_EQ(os2->UlUnicodeRange2(), OS2_UL_UNICODE_RANGE2); + EXPECT_EQ(os2->UlUnicodeRange3(), OS2_UL_UNICODE_RANGE3); + EXPECT_EQ(os2->UlUnicodeRange4(), OS2_UL_UNICODE_RANGE4); + + ByteVector vend_id; + os2->AchVendId(&vend_id); + EXPECT_EQ(vend_id.size(), sizeof(OS2_ACH_VEND_ID)); + for (size_t i = 0; i < vend_id.size(); ++i) { + EXPECT_EQ(vend_id[i], OS2_ACH_VEND_ID[i]); + } + + EXPECT_EQ(os2->FsSelection(), OS2_FS_SELECTION); + EXPECT_EQ(os2->UsFirstCharIndex(), OS2_US_FIRST_CHAR_IDX); + EXPECT_EQ(os2->UsLastCharIndex(), OS2_US_LAST_CHAR_IDX); + EXPECT_EQ(os2->STypoAscender(), OS2_STYPO_ASCENDER); + EXPECT_EQ(os2->STypoDescender(), OS2_STYPO_DESCENDER); + EXPECT_EQ(os2->STypoLineGap(), OS2_STYPO_LINE_GAP); + EXPECT_EQ(os2->UsWinAscent(), OS2_US_WIN_ASCENT); + EXPECT_EQ(os2->UsWinDescent(), OS2_US_WIN_DESCENT); + EXPECT_EQ(os2->UlCodePageRange1(), OS2_UL_CODE_PAGE_RANGE1); + EXPECT_EQ(os2->UlCodePageRange2(), OS2_UL_CODE_PAGE_RANGE2); + + // TODO(arthurhsu): SxHeight() not tested + // TODO(arthurhsu): SCapHeight() not tested + // TODO(arthurhsu): UsDefaultChar() not tested + // TODO(arthurhsu): UsBreakChar() not tested + // TODO(arthurhsu): UsMaxContext() not tested + + return true; +} + +bool VerifyOS_2(Table* original, Table* target) { + EXPECT_TRUE(VerifyOS_2(original)); + EXPECT_TRUE(VerifyOS_2(target)); + return true; +} + +} // namespace sfntly |