C# NetOffice: Fonts ersetzen (Word)

heulendoch

Ensign
Registriert
Feb. 2014
Beiträge
252
Hallo zusammen,

ich habe ein Dokument, es wurden darin folgende Schriften verwendet:
Calibri Light, Courier New, Times New Roman, Cambria
C#:
        public HashSet<string> GetUsedFonts(Document document) {

            var UsedFonts = new HashSet<string>();

            foreach(var CurrentParagraph in document.Paragraphs) {
                for(int i = 1; i <= CurrentParagraph.Range.Characters.Count; i++) {
                    UsedFonts.Add(CurrentParagraph.Range.Characters[i].Font.Name);
                }
            }

            return UsedFonts;
        }

Beispielhaft möchte ich die Schriften durch Arial tauschen. Das klappt auch bei Courier New und bei Cambria, bei den anderen aber nicht.
C#:
        public void ReplaceFonts() {

            var DocumentRange = WordApp.ActiveDocument.Range();

            //ReplaceFont(DocumentRange, "Cambria", "Times New Roman");
            //ReplaceFont(DocumentRange, "Calibri Light", "Times New Roman");
            //ReplaceFont(DocumentRange, "Courier New", "Times New Roman");

            ReplaceFont(DocumentRange, "Calibri Light", "Arial");
            ReplaceFont(DocumentRange, "Courier New", "Arial");
            ReplaceFont(DocumentRange, "Times New Roman", "Arial");
            ReplaceFont(DocumentRange, "Cambria", "Arial");


            DocumentRange.Find.ClearFormatting();
            DocumentRange.Find.Replacement.ClearFormatting();
        }

        private bool ReplaceFont(Range currentRange, string oldFont, string newFont) {

            currentRange.Select();

            currentRange.Find.ClearFormatting();
            currentRange.Find.Font.Name = oldFont;

            currentRange.Find.Replacement.ClearFormatting();
            currentRange.Find.Replacement.Font.Name = newFont;

            object arg1 = Missing.Value; //Find Pattern
            object arg2 = Missing.Value; //MatchCase
            object arg3 = Missing.Value; //MatchWholeWord
            object arg4 = Missing.Value; //MatchWildcards
            object arg5 = Missing.Value; //MatchSoundsLike
            object arg6 = Missing.Value; //MatchAllWordForms
            object arg7 = Missing.Value; //Forward
            object arg8 = Missing.Value; //Wrap
            object arg9 = Missing.Value; //Format
            object arg10 = Missing.Value; //ReplaceWith
            object arg11 = WdReplace.wdReplaceAll; //Replace
            object arg12 = Missing.Value; //MatchKashida
            object arg13 = Missing.Value; //MatchDiacritics
            object arg14 = Missing.Value; //MatchAlefHamza
            object arg15 = Missing.Value; //MatchControl

            var Result = currentRange.Find.Execute(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);

            if (Result) {
                Console.WriteLine("Ersetzung von " + oldFont + " erfolgt");
            }

            return Result;
        }

Leider komme ich einfach nicht drauf, weshalb es bei den anderen zwei Schriften nicht klappt?

Falls jemand ein paar Vorschläge hat, wäre super. Danke!
 

Anhänge

  • fonts-new.docx
    11,9 KB · Aufrufe: 158
Wenn Du dir die XLM fontsTable Datei in dem Word Dokument ansiehst, dann findest Du hier:
  • Arial
  • Times New Roman
  • Calibri Light
  • Consolas
  • Courier New
  • Cambria
  • Calibri
als Font Namen, welche in dem Dokument vorhanden sein sollten. Vermutlich findet deine Suche nicht alle Fonts, je nach Art der Attrbute Underline oder Bold, die verwendet werden?

Ich weiß es selbst nicht wirklich, finde die Frage aber interessant, warum dies so ist.
 
@tRITON In dem Dokument sind alle Textpassagen normalgestellt, ohne Auszeichnung.

Ich hab jetzt einfach mal direkt in VBA geschaut, aber auch hier funktioniert z. B. Courier New und die anderen nicht.
Code:
Sub MakroReplaceFont()

    Selection.find.ClearFormatting
    Selection.find.Font.name = "Courier New"
    Selection.find.Replacement.ClearFormatting
    Selection.find.Replacement.Font.name = "Arial"
    With Selection.find
        .text = ""
        .Replacement.text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchKashida = False
        .MatchDiacritics = False
        .MatchAlefHamza = False
        .MatchControl = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With
    Selection.find.Execute replace:=wdReplaceAll
End Sub

Ich hatte ja schon das Leerzeichen im Schriftnamen im Verdacht (man weiß ja nie) deshalb hatte ich speziell Courier New mit aufgenommen und getestet..
Ergänzung ()

Okay, das Word XML ist tatsächlich interessant
XML:
<!-- Absatz 1 -->
<w:p w:rsidR="00C85D0E" w:rsidRPr="00616DFB" w:rsidRDefault="00616DFB" w:rsidP="00616DFB"><w:pPr><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi" w:cstheme="majorHAnsi"/><w:sz w:val="24"/><w:szCs w:val="24"/></w:rPr></w:pPr><w:r w:rsidRPr="00616DFB"><w:rPr><w:rFonts w:asciiTheme="majorHAnsi" w:hAnsiTheme="majorHAnsi" w:cstheme="majorHAnsi"/><w:sz w:val="24"/><w:szCs w:val="24"/></w:rPr><w:t>Text</w:t></w:r></w:p>
<!-- Absatz 2 -->
<w:p w:rsidR="00C632E3" w:rsidRPr="00616DFB" w:rsidRDefault="00616DFB"><w:pPr><w:rPr><w:rFonts w:ascii="Courier New" w:hAnsi="Courier New" w:cs="Courier New"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r w:rsidRPr="00616DFB"><w:rPr><w:rFonts w:ascii="Courier New" w:hAnsi="Courier New" w:cs="Courier New"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US"/></w:rPr><w:t>Text</w:t></w:r></w:p>
<!-- Absatz 3 -->
<w:p w:rsidR="00C632E3" w:rsidRPr="00616DFB" w:rsidRDefault="00616DFB"><w:pPr><w:rPr><w:rFonts w:asciiTheme="majorBidi" w:hAnsiTheme="majorBidi" w:cstheme="majorBidi"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r w:rsidRPr="00616DFB"><w:rPr><w:rFonts w:asciiTheme="majorBidi" w:hAnsiTheme="majorBidi" w:cstheme="majorBidi"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US"/></w:rPr><w:t>Text</w:t></w:r></w:p>
<!-- Absatz 4 -->
<w:p w:rsidR="00616DFB" w:rsidRPr="00616DFB" w:rsidRDefault="00616DFB" w:rsidP="00616DFB"><w:pPr><w:rPr><w:rFonts w:asciiTheme="majorBidi" w:hAnsiTheme="majorBidi" w:cstheme="majorBidi"/><w:sz w:val="24"/><w:szCs w:val="24"/></w:rPr></w:pPr><w:r w:rsidRPr="00616DFB"><w:rPr><w:rFonts w:ascii="Cambria" w:eastAsia="Times New Roman" w:hAnsi="Cambria" w:cs="Times New Roman"/><w:sz w:val="24"/><w:szCs w:val="24"/><w:lang w:val="en-US" w:eastAsia="en-CA"/></w:rPr><w:t>Text</w:t></w:r></w:p>

Warum hier z. B. majorHAnsi bzw. majorBidi steht..
 
Zuletzt bearbeitet:
Wofür soll die Fingerübung denn gut sein?

Ansonsten halte ich den Ansatz für nicht gut. Formate sollten (und tun es out of the box auch) auf einem Design basieren, dort enthalten ist eine Schriftart. Du musst also schlussendlich nur die Abweichungen in jedem Absatz löschen und dann ein anderes Theme zuordnen wo Arial die zugeordnete Schriftart ist. Oder halt die Theme-Schriftart gemischt auf das aktuelle Theme anwenden.

Was du händisch in 5 Sekunden ohne Code bearbeitet bekommst ist
Strg+A
Strg+Leertaste (löscht alle abweichenden Zeichenformate)
Entwurf > Schriftarten > "Arial"

PS: Deine major und minor Schriftart sind die aus dem Design. Dazu gibts auch einen xml Part im Dokument wo die hinterlegt sind.
 

Anhänge

  • DocumentTheme.png
    DocumentTheme.png
    17,2 KB · Aufrufe: 158
Nein, das geht nicht, weil ich Probleme habe, wenn z. B. jemand Symbol Font benutzt und Auszeichnungen des Absatzes sollten natürlich vorhanden bleiben.

Gibt es eine Lösung für mein spezifisches Problem?
 
Ich würde es trotzdem zuerst über das Design probieren.
Dein erster Absatz benutzt nämlich die Calibri Light und das ist die major Schrift des im Dokument hinterlegten Designs. Dieser Absatz wird sich also ändern sobald das Design geändert wurde.

Als erstes holst du dir das aktuelle Theme des Dokuments.
Du gehst per foreach durch jeden Paragraph und checkst, ob der Font des Absatzes != dem Minor Font ist.
Wenn das der Fall ist, dann setze den Font auf den minor Font.
Nach der foreach setzt du das DokumentTheme oder die Schrift im Theme auf Arial.

Du brauchst eine Fallunterscheidung wenn es Überschriften gibt, dann musst du Abweichung auf major prüfen. Das geht per paragraph.getStyle()

Das sollte deine Absätze prinzipiell zurücksetzen aber Zeichenformate unberührt lassen.
Es würde ich aber schon anbieten für alle Abweichungen Zeichenformate zu verwenden, das macht das Leben leichter.

Allgemeiner Tipp:
C#:
var DocumentRange = WordApp.ActiveDocument.Range();
Lass das. VSTO ist kein reines C#. Du solltest alles sauber casten.
C#:
using MsoWord = Microsoft.Office.Interop.Word;
using MsoCore = Microsoft.Office.Core;
private readonly MsoWord.Document thisDocument;
So baue ich das mittlerweile auf. Der Hintergrund ist simpel aber fies.
Der Compiler verzeiht dir das fehlende Casting genau so lange wie du kein WPF verwendest. Sobald du auch nur ein WPF UserControl verwendest, schmeißt dir der Compiler alle fehlenden Castings vor die Füße.
 

Anhänge

  • DocumentTheme2.png
    DocumentTheme2.png
    15,7 KB · Aufrufe: 158
Ich schau mir das mal an, aber ist auch eher ein Workaround.. weshalb die Fontsuche nicht klappt ist einfach weird.

morcego schrieb:
Lass das. VSTO ist kein reines C#. Du solltest alles sauber casten.
Ich nutze kein VSTO. :)
 
Wie das Casten in nicht VSTO ist weiß ich nicht, wenn´s knallen sollte hast du den Ansatz. :D

Ich bin mir grad ehrlich gesagt selber nicht sicher ob das klappt, muss das auch mal probieren. Wäre jedenfalls ein nettes Feature auf der Liste für mein AddIn. :D
 
Ich habe es jetzt gefunden... ist doch ganz klar, wieso bin ich da nicht eher drauf gekommen?! Nicht deren ernst..

Code:
            ReplaceFont(DocumentRange, "+Überschriften", "Arial");
            ReplaceFont(DocumentRange, "+Überschriften CS", "Arial");
Findet die beiden Absätze.
 
Und ich hätts eben mal so gebastelt. Keinen Schimmer wie lange das bei einem 50 Seiten Dokument läuft.
Bei Office ist vieles "nicht deren ernst". :hammer_alt:
C#:
        public void SetParagraphFontToTheme()
        {
            MsoCore.OfficeTheme officeTheme = thisDocument.DocumentTheme;
            MsoCore.ThemeFontScheme themeFontScheme = officeTheme.ThemeFontScheme;
            MsoCore.ThemeFont minorFont = themeFontScheme.MinorFont.Item(MsoCore.MsoFontLanguageIndex.msoThemeLatin);
            foreach (MsoWord.Range character in thisDocument.Characters)
            {
                if (character.Font.Name != "Webdings")
                {
                    character.Font.Name = minorFont.Name;
                }
            }
        }
 
Danke für deine Hilfe, hat mich auf jeden Fall der Lösung sehr nahe gebracht!

Ich hab es jetzt so und werde es auch lassen, da dass ganze "Schriftblöcke" anspringt
C#:
public void ReplaceFonts() {

            var DocumentRange = WordApp.ActiveDocument.Range();

            var TargetFont = "Times New Roman";
            //var TargetFont = "Comic Sans MS";
            var CheckFonts = new string[] { "+Überschriften", "+Überschriften CS", "+Textkörper", "+Textkörper CS" };
            var ReplaceFonts = new List<string> { "Calibri", "Courier New", "Arial", "Cambria", "Calibri Light" };

            foreach(var Font in CheckFonts) {
                var RealFont = CheckFont(DocumentRange, Font);
              
                if (RealFont == null || RealFont == TargetFont)
                    continue;

                ReplaceFonts.Add(Font);
            }

            foreach(var Font in ReplaceFonts) {
                ReplaceFont(DocumentRange, Font, TargetFont);
            }

            DocumentRange.Find.ClearFormatting();
            DocumentRange.Find.Replacement.ClearFormatting();
        }

        private string CheckFont(Range currentRange, string oldFont) {
            currentRange.Find.ClearFormatting();
            currentRange.Find.Font.Name = oldFont;

            object arg1 = Missing.Value; //Find Pattern
            object arg2 = Missing.Value; //MatchCase
            object arg3 = Missing.Value; //MatchWholeWord
            object arg4 = Missing.Value; //MatchWildcards
            object arg5 = Missing.Value; //MatchSoundsLike
            object arg6 = Missing.Value; //MatchAllWordForms
            object arg7 = Missing.Value; //Forward
            object arg8 = WdFindWrap.wdFindContinue; //Wrap
            object arg9 = Missing.Value; //Format
            object arg10 = Missing.Value; //ReplaceWith
            object arg11 = WdReplace.wdReplaceNone; //Replace
            object arg12 = Missing.Value; //MatchKashida
            object arg13 = Missing.Value; //MatchDiacritics
            object arg14 = Missing.Value; //MatchAlefHamza
            object arg15 = Missing.Value; //MatchControl

            var Result = currentRange.Find.Execute(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);

            if (Result) {
                return WordApp.Selection.Font.Name;
            }

            return null;
        }

        private bool ReplaceFont(Range currentRange, string oldFont, string newFont) {

            currentRange.Find.ClearFormatting();
            currentRange.Find.Font.Name = oldFont;

            currentRange.Find.Replacement.ClearFormatting();
            currentRange.Find.Replacement.Font.Name = newFont;

            object arg1 = Missing.Value; //Find Pattern
            object arg2 = Missing.Value; //MatchCase
            object arg3 = Missing.Value; //MatchWholeWord
            object arg4 = Missing.Value; //MatchWildcards
            object arg5 = Missing.Value; //MatchSoundsLike
            object arg6 = Missing.Value; //MatchAllWordForms
            object arg7 = Missing.Value; //Forward
            object arg8 = Missing.Value; //Wrap
            object arg9 = Missing.Value; //Format
            object arg10 = Missing.Value; //ReplaceWith
            object arg11 = WdReplace.wdReplaceAll; //Replace
            object arg12 = Missing.Value; //MatchKashida
            object arg13 = Missing.Value; //MatchDiacritics
            object arg14 = Missing.Value; //MatchAlefHamza
            object arg15 = Missing.Value; //MatchControl

            var Result = currentRange.Find.Execute(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);

            if (Result) {
                Console.WriteLine("Ersetzung von " + oldFont + " erfolgt");
            }

            return Result;
        }

:D
 
Zurück
Oben