예제 #1
0
        public void InitializeFromImperator(
            Imperator.Provinces.Province impProvince,
            CultureMapper cultureMapper,
            ReligionMapper religionMapper
            )
        {
            ImperatorProvince = impProvince;

            // If we're initializing this from Imperator provinces, then having an owner or being a wasteland/sea is not a given -
            // there are uncolonized provinces in Imperator, also uninhabitables have culture and religion.

            var impOwnerCountry = ImperatorProvince.OwnerCountry.Value;

            if (impOwnerCountry is not null)
            {
                ownerTitle = impOwnerCountry.CK3Title;                 // linking to our holder's title
            }

            // Religion first
            SetReligionFromImperator(religionMapper);

            // Then culture
            SetCultureFromImperator(cultureMapper);

            // Holding type
            SetHoldingFromImperator();

            details.Buildings.Clear();
        }
예제 #2
0
        private static CultureInfo GetNumberCulture(TextRunProperties properties, out NumberSubstitutionMethod method, out bool ignoreUserOverride)
        {
            ignoreUserOverride = true;
            NumberSubstitution sub = properties.NumberSubstitution;

            if (sub == null)
            {
                method = NumberSubstitutionMethod.AsCulture;
                return(CultureMapper.GetSpecificCulture(properties.CultureInfo));
            }

            method = sub.Substitution;

            switch (sub.CultureSource)
            {
            case NumberCultureSource.Text:
                return(CultureMapper.GetSpecificCulture(properties.CultureInfo));

            case NumberCultureSource.User:
                ignoreUserOverride = false;
                return(CultureInfo.CurrentCulture);

            case NumberCultureSource.Override:
                return(sub.CultureOverride);
            }

            return(null);
        }
        /// <summary>
        /// Compute a shaped glyph run object from specified glyph-based info
        /// </summary>
        internal sealed override GlyphRun ComputeShapedGlyphRun(
            Point origin,
            char[]                   characterString,
            ushort[]                 clusterMap,
            ushort[]                 glyphIndices,
            IList <double> glyphAdvances,
            IList <Point> glyphOffsets,
            bool rightToLeft,
            bool sideways
            )
        {
            Invariant.Assert(_shapeTypeface != null);
            Invariant.Assert(glyphIndices != null);
            // Device fonts are only used through the LS non-glyphed code path. Only when a DigitCulture is set
            // will a potential device font be ignored and come through shaping.
            Invariant.Assert(_shapeTypeface.DeviceFont == null || _textItem.DigitCulture != null);

            bool[] caretStops = null;

            if (clusterMap != null &&
                (HasExtendedCharacter || NeedsCaretInfo)
                )
            {
                caretStops = new bool[clusterMap.Length + 1];

                // caret stops at cluster boundaries, the first and the last entries are always set
                caretStops[0] = true;
                caretStops[clusterMap.Length] = true;

                ushort lastGlyph = clusterMap[0];

                for (int i = 1; i < clusterMap.Length; i++)
                {
                    ushort glyph = clusterMap[i];

                    if (glyph != lastGlyph)
                    {
                        caretStops[i] = true;
                        lastGlyph     = glyph;
                    }
                }
            }

            return(GlyphRun.TryCreate(
                       _shapeTypeface.GlyphTypeface,
                       (rightToLeft ? 1 : 0),
                       sideways,
                       _emSize,
                       glyphIndices,
                       origin,
                       glyphAdvances,
                       glyphOffsets,
                       characterString,
                       null,
                       clusterMap,
                       caretStops,
                       XmlLanguage.GetLanguage(CultureMapper.GetSpecificCulture(_properties.CultureInfo).IetfLanguageTag),
                       _textFormattingMode
                       ));
        }
예제 #4
0
        public void ImportImperatorCharacters(Imperator.World impWorld,
                                              ReligionMapper religionMapper,
                                              CultureMapper cultureMapper,
                                              TraitMapper traitMapper,
                                              NicknameMapper nicknameMapper,
                                              LocDB locDB,
                                              ProvinceMapper provinceMapper,
                                              DeathReasonMapper deathReasonMapper,
                                              Date endDate,
                                              Date ck3BookmarkDate
                                              )
        {
            Logger.Info("Importing Imperator Characters...");

            foreach (var character in impWorld.Characters)
            {
                ImportImperatorCharacter(
                    character,
                    religionMapper,
                    cultureMapper,
                    traitMapper,
                    nicknameMapper,
                    locDB,
                    provinceMapper,
                    deathReasonMapper,
                    endDate,
                    ck3BookmarkDate
                    );
            }
            Logger.Info($"{Count} total characters recognized.");

            LinkMothersAndFathers();
            LinkSpouses();
            LinkPrisoners();
        }
예제 #5
0
        private void ImportImperatorCharacter(
            Imperator.Characters.Character character,
            ReligionMapper religionMapper,
            CultureMapper cultureMapper,
            TraitMapper traitMapper,
            NicknameMapper nicknameMapper,
            LocDB locDB,
            ProvinceMapper provinceMapper,
            DeathReasonMapper deathReasonMapper,
            Date endDate,
            Date ck3BookmarkDate
            )
        {
            // Create a new CK3 character
            var newCharacter = new Character(
                character,
                religionMapper,
                cultureMapper,
                traitMapper,
                nicknameMapper,
                locDB,
                provinceMapper,
                deathReasonMapper,
                endDate,
                ck3BookmarkDate
                );

            character.CK3Character = newCharacter;
            Add(newCharacter);
        }
예제 #6
0
        /// <summary>
        /// Compute unshaped glyph run object from the specified character-based info
        /// </summary>
        internal sealed override GlyphRun ComputeUnshapedGlyphRun(
            Point origin,
            char[]        characterString,
            IList <double> characterAdvances
            )
        {
            bool          nullFont;
            GlyphTypeface glyphTypeface = GetGlyphTypeface(out nullFont);

            Invariant.Assert(glyphTypeface != null);

            return(glyphTypeface.ComputeUnshapedGlyphRun(
                       origin,
                       new CharacterBufferRange(
                           characterString,
                           0, // offsetToFirstChar
                           characterString.Length
                           ),
                       characterAdvances,
                       _emSize,
                       (float)_properties.PixelsPerDip,
                       _properties.FontHintingEmSize,
                       nullFont,
                       CultureMapper.GetSpecificCulture(_properties.CultureInfo),
                       (_shapeTypeface == null || _shapeTypeface.DeviceFont == null) ? null : _shapeTypeface.DeviceFont.Name,
                       _textFormattingMode
                       ));
        }
예제 #7
0
        private void ImportImperatorCountry(
            Country country,
            CountryCollection imperatorCountries,
            TagTitleMapper tagTitleMapper,
            LocDB locDB,
            ProvinceMapper provinceMapper,
            CoaMapper coaMapper,
            GovernmentMapper governmentMapper,
            SuccessionLawMapper successionLawMapper,
            DefiniteFormMapper definiteFormMapper,
            ReligionMapper religionMapper,
            CultureMapper cultureMapper,
            NicknameMapper nicknameMapper,
            CharacterCollection characters,
            Date conversionDate
            )
        {
            // Create a new title or update existing title
            var name = DetermineName(country, imperatorCountries, tagTitleMapper, locDB);

            if (TryGetValue(name, out var existingTitle))
            {
                existingTitle.InitializeFromTag(
                    country,
                    imperatorCountries,
                    locDB,
                    provinceMapper,
                    coaMapper,
                    governmentMapper,
                    successionLawMapper,
                    definiteFormMapper,
                    religionMapper,
                    cultureMapper,
                    nicknameMapper,
                    characters,
                    conversionDate
                    );
            }
            else
            {
                Add(
                    country,
                    imperatorCountries,
                    locDB,
                    provinceMapper,
                    coaMapper,
                    tagTitleMapper,
                    governmentMapper,
                    successionLawMapper,
                    definiteFormMapper,
                    religionMapper,
                    cultureMapper,
                    nicknameMapper,
                    characters,
                    conversionDate
                    );
            }
        }
예제 #8
0
        public void CultureMatchesWithReligion()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = qwe imp = test imp = poi religion = thereligion }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Equal("culture", culMapper.Match("test", "thereligion", 56, 49, "e_title"));
        }
예제 #9
0
        public void SimpleCultureCorrectlyMatches()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = qwe imp = test imp = poi }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Equal("culture", culMapper.Match("test", "", 56, 49, "e_title"));
        }
예제 #10
0
        public void CultureFailsWithNoReligion()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = qwe imp = test imp = poi religion = thereligion }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Null(culMapper.Match("test", "", 56, 49, "e_title"));
        }
예제 #11
0
        public void SimpleCultureMatchesWithNonReligiousMatch()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = test }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Equal("culture", culMapper.NonReligiousMatch("test", "", 56, 49, "e_title"));
        }
예제 #12
0
        public void CultureFailsWithWrongCapital()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = qwe imp = test imp = poi religion = thereligion ck3Province = 4 }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Null(culMapper.Match("test", "thereligion", 3, 49, "e_title"));
        }
예제 #13
0
        public void NonMatchGivesEmptyOptional()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = culture }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Null(culMapper.Match("nonMatchingCulture", "", 56, 49, "e_title"));
        }
예제 #14
0
        public void CultureMatchesWithOwnerTitle()
        {
            var reader = new BufferedReader(
                "link = { ck3 = culture imp = qwe imp = test imp = poi religion = thereligion ck3Province = 4 owner = e_roman_empire }"
                );
            var culMapper = new CultureMapper(reader);

            Assert.Equal("culture", culMapper.Match("test", "thereligion", 4, 49, "e_roman_empire"));
        }
예제 #15
0
        public void SetHoldingLogicWorks()
        {
            var reader1 = new BufferedReader(" = { province_rank=city_metropolis }");
            var reader2 = new BufferedReader(" = { province_rank=city fort=yes }");
            var reader3 = new BufferedReader(" = { province_rank=city }");
            var reader4 = new BufferedReader(" = { province_rank=settlement holy_site = 69 fort=yes }");
            var reader5 = new BufferedReader(" = { province_rank=settlement fort=yes }");
            var reader6 = new BufferedReader(" = { province_rank=settlement }");

            var imperatorCountry = new ImperatorToCK3.Imperator.Countries.Country(1);
            var impProvince      = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader1, 42);

            impProvince.LinkOwnerCountry(imperatorCountry);
            var impProvince2 = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader2, 43);

            impProvince2.LinkOwnerCountry(imperatorCountry);
            var impProvince3 = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader3, 44);

            impProvince3.LinkOwnerCountry(imperatorCountry);
            var impProvince4 = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader4, 45);

            impProvince4.LinkOwnerCountry(imperatorCountry);
            var impProvince5 = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader5, 46);

            impProvince5.LinkOwnerCountry(imperatorCountry);
            var impProvince6 = ImperatorToCK3.Imperator.Provinces.Province.Parse(reader6, 47);

            impProvince6.LinkOwnerCountry(imperatorCountry);

            var province1 = new Province();
            var province2 = new Province();
            var province3 = new Province();
            var province4 = new Province();
            var province5 = new Province();
            var province6 = new Province();

            var landedTitles   = new Title.LandedTitles();
            var cultureMapper  = new CultureMapper();
            var religionMapper = new ReligionMapper();

            province1.InitializeFromImperator(impProvince, landedTitles, cultureMapper, religionMapper);
            province2.InitializeFromImperator(impProvince2, landedTitles, cultureMapper, religionMapper);
            province3.InitializeFromImperator(impProvince3, landedTitles, cultureMapper, religionMapper);
            province4.InitializeFromImperator(impProvince4, landedTitles, cultureMapper, religionMapper);
            province5.InitializeFromImperator(impProvince5, landedTitles, cultureMapper, religionMapper);
            province6.InitializeFromImperator(impProvince6, landedTitles, cultureMapper, religionMapper);

            Assert.Equal("city_holding", province1.Holding);
            Assert.Equal("castle_holding", province2.Holding);
            Assert.Equal("city_holding", province3.Holding);
            Assert.Equal("church_holding", province4.Holding);
            Assert.Equal("castle_holding", province5.Holding);
            Assert.Equal("none", province6.Holding);
        }
예제 #16
0
        public void ImportImperatorCountries(
            CountryCollection imperatorCountries,
            TagTitleMapper tagTitleMapper,
            LocDB locDB,
            ProvinceMapper provinceMapper,
            CoaMapper coaMapper,
            GovernmentMapper governmentMapper,
            SuccessionLawMapper successionLawMapper,
            DefiniteFormMapper definiteFormMapper,
            ReligionMapper religionMapper,
            CultureMapper cultureMapper,
            NicknameMapper nicknameMapper,
            CharacterCollection characters,
            Date conversionDate
            )
        {
            Logger.Info("Importing Imperator Countries...");

            // landedTitles holds all titles imported from CK3. We'll now overwrite some and
            // add new ones from Imperator tags.
            var counter = 0;

            // We don't need pirates, barbarians etc.
            foreach (var country in imperatorCountries.Where(c => c.CountryType == CountryType.real))
            {
                ImportImperatorCountry(
                    country,
                    imperatorCountries,
                    tagTitleMapper,
                    locDB,
                    provinceMapper,
                    coaMapper,
                    governmentMapper,
                    successionLawMapper,
                    definiteFormMapper,
                    religionMapper,
                    cultureMapper,
                    nicknameMapper,
                    characters,
                    conversionDate
                    );
                ++counter;
            }
            Logger.Info($"Imported {counter} countries from I:R.");
        }
예제 #17
0
        public QuotesManager()
        {
            InitializeComponent();

            playlist      = new Playlist();
            cultureMapper = new CultureMapper {
                new EnglishQuoteCollectorRules()
            };
            playlistSerializer = new XmlPlaylistSerializer(playlist);

            if (File.Exists(QuotesDatabaseFile))
            {
                try { playlistSerializer.LoadAsync(QuotesDatabaseFile); }
                catch (FileNotFoundException) { }
            }

            lookupManager = new LookupManager(playlist, cultureMapper);

            InitializeEventHandlers();

            Loaded += QuotesManager_Loaded;
        }
예제 #18
0
    public RulerTerm(
        Imperator.Countries.RulerTerm imperatorRulerTerm,
        Characters.CharacterCollection characters,
        GovernmentMapper governmentMapper,
        LocDB locDB,
        ReligionMapper religionMapper,
        CultureMapper cultureMapper,
        NicknameMapper nicknameMapper,
        ProvinceMapper provinceMapper
        )
    {
        if (imperatorRulerTerm.CharacterId is not null)
        {
            CharacterId = $"imperator{imperatorRulerTerm.CharacterId}";
        }
        StartDate = imperatorRulerTerm.StartDate;
        if (imperatorRulerTerm.Government is not null)
        {
            Government = governmentMapper.GetCK3GovernmentForImperatorGovernment(imperatorRulerTerm.Government);
        }

        PreImperatorRuler = imperatorRulerTerm.PreImperatorRuler;
        if (PreImperatorRuler?.Country is not null)
        {
            // create a new ruler character
            var character = new Character(
                PreImperatorRuler,
                StartDate,
                PreImperatorRuler.Country,
                locDB,
                religionMapper,
                cultureMapper,
                nicknameMapper,
                provinceMapper
                );
            characters.Add(character);
            CharacterId = character.Id;
        }
    }
예제 #19
0
        public Title Add(
            Country country,
            CountryCollection imperatorCountries,
            LocDB locDB,
            ProvinceMapper provinceMapper,
            CoaMapper coaMapper,
            TagTitleMapper tagTitleMapper,
            GovernmentMapper governmentMapper,
            SuccessionLawMapper successionLawMapper,
            DefiniteFormMapper definiteFormMapper,
            ReligionMapper religionMapper,
            CultureMapper cultureMapper,
            NicknameMapper nicknameMapper,
            CharacterCollection characters,
            Date conversionDate
            )
        {
            var newTitle = new Title(this,
                                     country,
                                     imperatorCountries,
                                     locDB,
                                     provinceMapper,
                                     coaMapper,
                                     tagTitleMapper,
                                     governmentMapper,
                                     successionLawMapper,
                                     definiteFormMapper,
                                     religionMapper,
                                     cultureMapper,
                                     nicknameMapper,
                                     characters,
                                     conversionDate
                                     );

            dict[newTitle.Id] = newTitle;
            return(newTitle);
        }
예제 #20
0
 private Title(LandedTitles parentCollection,
               Country country,
               CountryCollection imperatorCountries,
               LocDB locDB,
               ProvinceMapper provinceMapper,
               CoaMapper coaMapper,
               TagTitleMapper tagTitleMapper,
               GovernmentMapper governmentMapper,
               SuccessionLawMapper successionLawMapper,
               DefiniteFormMapper definiteFormMapper,
               ReligionMapper religionMapper,
               CultureMapper cultureMapper,
               NicknameMapper nicknameMapper,
               CharacterCollection characters,
               Date conversionDate
               )
 {
     this.parentCollection = parentCollection;
     Id = DetermineName(country, imperatorCountries, tagTitleMapper, locDB);
     SetRank();
     InitializeFromTag(
         country,
         imperatorCountries,
         locDB,
         provinceMapper,
         coaMapper,
         governmentMapper,
         successionLawMapper,
         definiteFormMapper,
         religionMapper,
         cultureMapper,
         nicknameMapper,
         characters,
         conversionDate
         );
 }
예제 #21
0
        /// <summary>
        /// Get text immediately preceding cpLimit.
        /// </summary>
        internal TextSpan <CultureSpecificCharacterBufferRange> GetPrecedingText(TextSource textSource, int cpLimit)
        {
            if (cpLimit > 0)
            {
                SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition);
                if (textRunSpanRider.At(cpLimit - 1))
                {
                    CharacterBufferRange charString = CharacterBufferRange.Empty;
                    CultureInfo          culture    = null;

                    TextRun run = textRunSpanRider.CurrentElement as TextRun;

                    if (run != null)
                    {
                        // Only TextRun containing text would have non-empty Character buffer range.
                        if (TextRunInfo.GetRunType(run) == Plsrun.Text &&
                            run.CharacterBufferReference.CharacterBuffer != null)
                        {
                            charString = new CharacterBufferRange(
                                run.CharacterBufferReference,
                                cpLimit - textRunSpanRider.CurrentSpanStart);

                            culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo);
                        }

                        return(new TextSpan <CultureSpecificCharacterBufferRange>(
                                   cpLimit - textRunSpanRider.CurrentSpanStart, // cp length
                                   new CultureSpecificCharacterBufferRange(culture, charString)
                                   ));
                    }
                }
            }

            // not in cache so call back to client
            return(textSource.GetPrecedingText(cpLimit));
        }
예제 #22
0
        /// <summary>
        /// Break a run of text into individually shape items.
        /// Shape items are delimited by
        ///     Change of writing system
        ///     Change of glyph typeface
        /// </summary>
        IList <TextShapeableSymbols> ITextSymbols.GetTextShapeableSymbols(
            GlyphingCache glyphingCache,
            CharacterBufferReference characterBufferReference,
            int length,
            bool rightToLeft,
            bool isRightToLeftParagraph,
            CultureInfo digitCulture,
            TextModifierScope textModifierScope,
            TextFormattingMode textFormattingMode,
            bool isSideways
            )
        {
            if (characterBufferReference.CharacterBuffer == null)
            {
                throw new ArgumentNullException("characterBufferReference.CharacterBuffer");
            }

            int offsetToFirstChar = characterBufferReference.OffsetToFirstChar - _characterBufferReference.OffsetToFirstChar;

            Debug.Assert(characterBufferReference.CharacterBuffer == _characterBufferReference.CharacterBuffer);
            Debug.Assert(offsetToFirstChar >= 0 && offsetToFirstChar < _length);

            if (length < 0 ||
                offsetToFirstChar + length > _length)
            {
                length = _length - offsetToFirstChar;
            }

            // Get the actual text run properties in effect, after invoking any
            // text modifiers that may be in scope.
            TextRunProperties textRunProperties = _textRunProperties;

            if (textModifierScope != null)
            {
                textRunProperties = textModifierScope.ModifyProperties(textRunProperties);
            }

            if (!rightToLeft)
            {
                // Fast loop early out for run with all non-complex characters
                // which can be optimized by not going thru shaping engine.

                int nominalLength;

                if (textRunProperties.Typeface.CheckFastPathNominalGlyphs(
                        new CharacterBufferRange(characterBufferReference, length),
                        textRunProperties.FontRenderingEmSize,
                        (float)textRunProperties.PixelsPerDip,
                        1.0,
                        double.MaxValue, // widthMax
                        true,            // keepAWord
                        digitCulture != null,
                        CultureMapper.GetSpecificCulture(textRunProperties.CultureInfo),
                        textFormattingMode,
                        isSideways,
                        false, //breakOnTabs
                        out nominalLength
                        ) && length == nominalLength)
                {
                    return(new TextShapeableCharacters[]
                    {
                        new TextShapeableCharacters(
                            new CharacterBufferRange(characterBufferReference, nominalLength),
                            textRunProperties,
                            textRunProperties.FontRenderingEmSize,
                            new MS.Internal.Text.TextInterface.ItemProps(),
                            null,   // shapeTypeface (no shaping required)
                            false,  // nullShape,
                            textFormattingMode,
                            isSideways
                            )
                    });
                }
            }

            IList <TextShapeableSymbols> shapeables = new List <TextShapeableSymbols>(2);

            glyphingCache.GetShapeableText(
                textRunProperties.Typeface,
                characterBufferReference,
                length,
                textRunProperties,
                digitCulture,
                isRightToLeftParagraph,
                shapeables,
                this as IShapeableTextCollector,
                textFormattingMode
                );

            return(shapeables);
        }
예제 #23
0
    public void ImportImperatorProvinces(Imperator.World impWorld, Title.LandedTitles titles, CultureMapper cultureMapper, ReligionMapper religionMapper, ProvinceMapper provinceMapper)
    {
        Logger.Info("Importing Imperator Provinces...");
        var counter = 0;

        // Imperator provinces map to a subset of CK3 provinces. We'll only rewrite those we are responsible for.
        foreach (var province in this)
        {
            var impProvinces = provinceMapper.GetImperatorProvinceNumbers(province.Id);
            // Provinces we're not affecting will not be in this list.
            if (impProvinces.Count == 0)
            {
                continue;
            }
            // Next, we find what province to use as its initializing source.
            var sourceProvince = DetermineProvinceSource(impProvinces, impWorld);
            if (sourceProvince is null)
            {
                Logger.Warn($"Could not determine source province for CK3 province {province.Id}!");
                continue;                 // MISMAP, or simply have mod provinces loaded we're not using.
            }
            province.InitializeFromImperator(sourceProvince.Value.Value, titles, cultureMapper, religionMapper);
            // And finally, initialize it.
            ++counter;
        }
        Logger.Info($"{impWorld.Provinces.Count} Imperator provinces imported into {counter} CK3 provinces.");
    }
예제 #24
0
        /// <summary>
        /// Fetch cached textrun
        /// </summary>
        internal TextRun FetchTextRun(
            FormatSettings settings,
            int cpFetch,
            int cpFirst,
            out int offsetToFirstCp,
            out int runLength
            )
        {
            SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, cpFetch);

            _latestPosition = textRunSpanRider.SpanPosition;
            TextRun textRun = (TextRun)textRunSpanRider.CurrentElement;

            if (textRun == null)
            {
                // run not already cached, fetch new run and cache it

                textRun = settings.TextSource.GetTextRun(cpFetch);

                if (textRun.Length < 1)
                {
                    throw new ArgumentOutOfRangeException("textRun.Length", SR.Get(SRID.ParameterMustBeGreaterThanZero));
                }

                Plsrun plsrun = TextRunInfo.GetRunType(textRun);

                if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject)
                {
                    TextRunProperties properties = textRun.Properties;

                    if (properties == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.TextRunPropertiesCannotBeNull));
                    }

                    if (properties.FontRenderingEmSize <= 0)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties"));
                    }

                    double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm;

                    if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize));
                    }

                    CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo);

                    if (culture == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties"));
                    }

                    if (properties.Typeface == null)
                    {
                        throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties"));
                    }
                }


                //
                // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be
                // consistent with the positions encoded by SpanVector. In run cache, the begining of a span
                // should always correspond to the begining of a cached text run. If the end of the currently fetched
                // run overlaps with the begining of an already cached run, the begining of the cached run needs to be
                // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range
                // so we'll simply remove the overlapped runs here.
                //

                // Move the rider to the end of the current run
                textRunSpanRider.At(cpFetch + textRun.Length - 1);
                _latestPosition = textRunSpanRider.SpanPosition;

                if (textRunSpanRider.CurrentElement != _textRunVector.Default)
                {
                    // The end overlaps with one or more cached runs, clear the range from the
                    // begining of the current fetched run to the end of the last overlapped cached run.
                    _latestPosition = _textRunVector.SetReference(
                        cpFetch,
                        textRunSpanRider.CurrentPosition + textRunSpanRider.Length - cpFetch,
                        _textRunVector.Default,
                        _latestPosition
                        );
                }

                _latestPosition = _textRunVector.SetReference(cpFetch, textRun.Length, textRun, _latestPosition);

                // Refresh the rider's SpanPosition following previous SpanVector.SetReference calls
                textRunSpanRider.At(_latestPosition, cpFetch);
            }

            // If the TextRun was obtained from the cache, make sure it has the right PixelsPerDip set on its properties.

            if (textRun.Properties != null)
            {
                textRun.Properties.PixelsPerDip = settings.TextSource.PixelsPerDip;
            }

            offsetToFirstCp = textRunSpanRider.CurrentPosition - textRunSpanRider.CurrentSpanStart;
            runLength       = textRunSpanRider.Length;
            Debug.Assert(textRun != null && runLength > 0, "Invalid run!");

            bool isText = textRun is ITextSymbols;

            if (isText)
            {
                // Chop text run to optimal length so we dont spend forever analysing
                // them all at once.

                int looseCharLength = TextStore.TypicalCharactersPerLine - cpFetch + cpFirst;

                if (looseCharLength <= 0)
                {
                    // this line already exceeds typical line length, incremental fetch goes
                    // about a quarter of the typical length.

                    looseCharLength = (int)Math.Round(TextStore.TypicalCharactersPerLine * 0.25);
                }

                if (runLength > looseCharLength)
                {
                    if (TextRunInfo.GetRunType(textRun) == Plsrun.Text)
                    {
                        //
                        // When chopping the run at the typical line length,
                        // - don't chop in between of higher & lower surrogate
                        // - don't chop combining mark away from its base character
                        // - don't chop joiner from surrounding characters
                        //
                        // Starting from the initial chopping point, we look ahead to find a safe position. We stop at
                        // a limit in case the run consists of many combining mark & joiner. That is rare and doesn't make
                        // much sense in shaping already.
                        //

                        CharacterBufferReference charBufferRef = textRun.CharacterBufferReference;

                        // We look ahead by one more line at most. It is not normal to have
                        // so many combining mark or joiner characters in a row. It doesn't make sense to
                        // look further if so.
                        int lookAheadLimit = Math.Min(runLength, looseCharLength + TextStore.TypicalCharactersPerLine);

                        int  sizeOfChar = 0;
                        int  endOffset  = 0;
                        bool canBreakAfterPrecedingChar = false;

                        for (endOffset = looseCharLength - 1; endOffset < lookAheadLimit; endOffset += sizeOfChar)
                        {
                            CharacterBufferRange charString = new CharacterBufferRange(
                                charBufferRef.CharacterBuffer,
                                charBufferRef.OffsetToFirstChar + offsetToFirstCp + endOffset,
                                runLength - endOffset
                                );

                            int ch = Classification.UnicodeScalar(charString, out sizeOfChar);

                            // We can only safely break if the preceding char is not a joiner character (i.e. can-break-after),
                            // and the current char is not combining or joiner (i.e. can-break-before).
                            if (canBreakAfterPrecedingChar && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch))
                            {
                                break;
                            }

                            canBreakAfterPrecedingChar = !Classification.IsJoiner(ch);
                        }

                        looseCharLength = Math.Min(runLength, endOffset);
                    }

                    runLength = looseCharLength;
                }
            }


            Debug.Assert(

                // valid run found
                runLength > 0

                // non-text run always fetched at run start
                && (isText ||
                    textRunSpanRider.CurrentSpanStart - textRunSpanRider.CurrentPosition == 0)

                // span rider of both text and format point to valid position
                && (textRunSpanRider.Length > 0 && textRunSpanRider.CurrentElement != null),

                "Text run fetching error!"
                );

            return(textRun);
        }
예제 #25
0
    public void InitializeFromTag(
        Country country,
        CountryCollection imperatorCountries,
        LocDB locDB,
        ProvinceMapper provinceMapper,
        CoaMapper coaMapper,
        GovernmentMapper governmentMapper,
        SuccessionLawMapper successionLawMapper,
        DefiniteFormMapper definiteFormMapper,
        ReligionMapper religionMapper,
        CultureMapper cultureMapper,
        NicknameMapper nicknameMapper,
        CharacterCollection characters,
        Date conversionDate
        )
    {
        IsImportedOrUpdatedFromImperator = true;
        ImperatorCountry          = country;
        ImperatorCountry.CK3Title = this;

        LocBlock?validatedName = GetValidatedName(country, imperatorCountries, locDB);

        HasDefiniteForm.Value    = definiteFormMapper.IsDefiniteForm(ImperatorCountry.Name);
        RulerUsesTitleName.Value = false;

        PlayerCountry = ImperatorCountry.PlayerCountry;

        ClearHolderSpecificHistory();

        FillHolderAndGovernmentHistory();

        // ------------------ determine color
        var color1Opt = ImperatorCountry.Color1;

        if (color1Opt is not null)
        {
            Color1 = color1Opt;
        }
        var color2Opt = ImperatorCountry.Color2;

        if (color2Opt is not null)
        {
            Color2 = color2Opt;
        }

        // determine successions laws
        history.InternalHistory.AddFieldValue("succession_laws",
                                              successionLawMapper.GetCK3LawsForImperatorLaws(ImperatorCountry.GetLaws()),
                                              conversionDate,
                                              "succession_laws"
                                              );

        // determine CoA
        CoA = coaMapper.GetCoaForFlagName(ImperatorCountry.Flag);

        // determine other attributes
        var srcCapital = ImperatorCountry.Capital;

        if (srcCapital is not null)
        {
            var provMappingsForImperatorCapital = provinceMapper.GetCK3ProvinceNumbers((ulong)srcCapital);
            if (provMappingsForImperatorCapital.Count > 0)
            {
                var foundCounty = parentCollection.GetCountyForProvince(provMappingsForImperatorCapital[0]);
                if (foundCounty is not null)
                {
                    CapitalCounty = foundCounty;
                }
            }
        }

        // determine country name localization
        var nameSet = false;

        if (validatedName is not null)
        {
            var nameLocBlock = Localizations.AddLocBlock(Id);
            nameLocBlock.CopyFrom(validatedName);
            nameSet = true;
        }
        if (!nameSet)
        {
            var impTagLoc = locDB.GetLocBlockForKey(ImperatorCountry.Tag);
            if (impTagLoc is not null)
            {
                var nameLocBlock = Localizations.AddLocBlock(Id);
                nameLocBlock.CopyFrom(impTagLoc);
                nameSet = true;
            }
        }
        if (!nameSet)
        {
            // use unlocalized name if not empty
            var name = ImperatorCountry.Name;
            if (!string.IsNullOrEmpty(name))
            {
                Logger.Warn($"Using unlocalized Imperator name {name} as name for {Id}!");
                var nameLocBlock = Localizations.AddLocBlock(Id);
                nameLocBlock["english"] = name;
                nameLocBlock.FillMissingLocWithBaseLanguageLoc();
                nameSet = true;
            }
        }
        // giving up
        if (!nameSet)
        {
            Logger.Warn($"{Id} needs help with localization! {ImperatorCountry.Name}?");
        }

        // determine adjective localization
        TrySetAdjectiveLoc(locDB, imperatorCountries);

        void FillHolderAndGovernmentHistory()
        {
            // ------------------ determine previous and current holders
            // there was no 0 AD, but year 0 works in game and serves well for adding BC characters to holder history
            var firstPossibleDate = new Date(0, 1, 1);

            foreach (var impRulerTerm in ImperatorCountry.RulerTerms)
            {
                var rulerTerm = new RulerTerm(
                    impRulerTerm,
                    characters,
                    governmentMapper,
                    locDB,
                    religionMapper,
                    cultureMapper,
                    nicknameMapper,
                    provinceMapper
                    );

                var characterId = rulerTerm.CharacterId;
                var gov         = rulerTerm.Government;

                var startDate = new Date(rulerTerm.StartDate);
                if (startDate < firstPossibleDate)
                {
                    startDate = new Date(firstPossibleDate);                     // TODO: remove this workaround if CK3 supports negative dates
                    firstPossibleDate.ChangeByDays(1);
                }

                history.InternalHistory.AddFieldValue("holder", characterId, startDate, "holder");
                if (gov is not null)
                {
                    history.InternalHistory.AddFieldValue("government", gov, startDate, "government");
                }
            }

            if (ImperatorCountry.Government is not null)
            {
                var lastCK3TermGov = history.GetGovernment(conversionDate);
                var ck3CountryGov  = governmentMapper.GetCK3GovernmentForImperatorGovernment(ImperatorCountry.Government);
                if (lastCK3TermGov != ck3CountryGov && ck3CountryGov is not null)
                {
                    history.InternalHistory.AddFieldValue("government", ck3CountryGov, conversionDate, "government");
                }
            }
        }
    }