简体   繁体   English

如何确定在C#中使用Word Office Interop打开的活动文档的字体颜色?

[英]How to determine the font color of the active document opened using word office interop in c#?

I have this code 我有这个代码

Word.Application WordApp = new Word.Application();
                WordApp.Visible = true;
                aDoc = WordApp.Documents.Open(fileName, ReadOnly: readOnly, Visible: isVisible);
                aDoc.Activate();

I wanted to determine the font color used in that opened document, Is there a way to do that? 我想确定打开的文档中使用的字体颜色,有没有办法做到这一点? Thank you! 谢谢!

There might not be a single color that is used for all the text. 可能没有一种颜色用于所有文本。 However, you can access the color information, maintain a count of the occurrence of each color, and then pick the maximum. 但是,您可以访问颜色信息,维护每种颜色的出现次数,然后选择最大值。

The basic idea is that for each Range , get the Font , and for each Font , use its Color property. 基本思想是,对于每个Range ,获取Font ,对于每个Font ,使用其Color属性。 When accessing the ranges, it seems the choice is to iterate over the characters. 访问范围时,似乎选择是遍历字符。 Perhaps there is a better way, but I couldn't find it. 也许有更好的方法,但是我找不到。 To me, it seems somewhat inefficient to have to iterate over each character range to get its color versus the ability to access continuous blocks of text that have the same style or color formatting. 对我来说,遍历每个字符范围以获取其颜色似乎有点效率低下,而访问具有相同样式或颜色格式的连续文本块的能力似乎有些低效。

Also, the interop Font.Color property is not straightforward to translate to System.Drawing.Color . 此外,interop的Font.Color属性也不Font.Color直接转换为System.Drawing.Color I found that .doc behaved as expected. 我发现.doc表现符合预期。 However, for .docx the ColorTranslator.FromOle(font.Color) did not return the correct colors in some cases. 但是,对于.docx ,在某些情况下ColorTranslator.FromOle(font.Color)不能返回正确的颜色。

After some digging, I found an RgbColorConverter , which was copied from a link found in this thread: Office 2007 [and higher] interop: retrieve RGB-color 经过一番挖掘,我发现了RgbColorConverter ,它是从该线程中的链接复制的: Office 2007 [及更高版本] interop:检索RGB-color

I thought it would be useful to reproduce here. 我认为在此处复制将很有用。

public static class WordColorTest {
    public static void GetColors() {

        Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();
        Documents docs = app.Documents;
        Document doc = docs.Open("C:\\temp\\ColorTest3.docx", ReadOnly:true);
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("<html><body>");
        sb.AppendLine("<table border=1 style='font-family:Arial;'>");
        sb.AppendLine("<tr><th>Text</th><th>RGB</th><th>Color</th></tr>");
        Dictionary<Color,int> counts = new Dictionary<Color,int>();

        foreach (Range rng in doc.StoryRanges) {
            foreach (Range rngChar in rng.Characters) { // by each character
                _Font f = rngChar.Font;
                int rgb = (int) f.Color;
                //System.Drawing.Color col = System.Drawing.ColorTranslator.FromOle(rgb); // do not use - not 1-to-1
                var col = RgbColorRetriever.GetRGBColor(f.Color, doc);
                //ColorFormat cf = f.TextColor; // error - exception on ".doc" files
                sb.AppendLine(String.Format("<tr><td>{0}</td><td>{1},{2},{3}</td><td bgcolor='{4}'></td></tr>", rngChar.Text, col.R, col.G, col.B, ToHex(col)));
                //sb.AppendLine(rngChar.Text + " " + col.R + "/" + col.G + "/" + col.B);

                if (f.Color != WdColor.wdColorAutomatic) {
                    if (counts.ContainsKey(col))
                        counts[col]++;
                    else
                        counts[col] = 1;
                }

                Marshal.ReleaseComObject(f);
                Marshal.ReleaseComObject(rngChar);
            }
            Marshal.ReleaseComObject(rng);
        }
        sb.AppendLine(String.Format("<tr><td colspan=3>&nbsp;</td></tr>"));

        foreach (Range rng in doc.StoryRanges) {
            foreach (Range rngWord in rng.Words) { // by each word
                _Font f = rngWord.Font;
                int rgb = (int) f.Color;
                //System.Drawing.Color col = System.Drawing.ColorTranslator.FromOle(rgb);  // do not use - not 1-to-1
                var col = RgbColorRetriever.GetRGBColor(f.Color, doc);
                sb.AppendLine(String.Format("<tr><td>{0}</td><td>{1},{2},{3}</td><td bgcolor='{4}'></td></tr>", rngWord.Text, col.R, col.G, col.B, ToHex(col)));
                //sb.AppendLine(rngWord.Text + " " + col.R + "/" + col.G + "/" + col.B);
                Marshal.ReleaseComObject(f);
                Marshal.ReleaseComObject(rngWord);
            }
            Marshal.ReleaseComObject(rng);
        }
        sb.AppendLine("</table>");
        sb.AppendLine("</body></html>");

        doc.Close(false);
        app.Quit(false);
        Marshal.ReleaseComObject(doc);
        Marshal.ReleaseComObject(docs);
        Marshal.ReleaseComObject(app);

        // if there is a tie, then there is the risk that the color will flip back and forth depending on how the dictionary is ordered
        Color max = counts.Count == 0 ? Color.Black : counts.Aggregate((i1,i2) => i1.Value > i2.Value ? i1 : i2).Key;
        String html = sb.ToString(); // testing only
    }

    private static String ToHex(System.Drawing.Color c) {
        return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2");
    }
}





/// <summary>
/// static class with rgb color retrieving logic: Microsoft.Office.Interop.Word.WdColor -> System.Drawing.Color
/// </summary>
public static class RgbColorRetriever {
    #region Constants

    // first byte of WdColor determines its format type
    private static readonly int
        RGB = 0x00,
        Automatic = 0xFF,
        System = 0x80,
        ThemeLow = 0xD0,
        ThemeHigh = 0xDF;

    //structure to store HSL (hue, saturation, lightness) color
    private struct HSL {
        public double H, S, L;
    }

    #endregion

    /// <summary>
    /// Get RGB-color from WdColor
    /// </summary>
    /// <param name="wdColor">source color</param>
    /// <param name="doc">document, where this color from (for appropriate color theme)</param>
    public static Color GetRGBColor(WdColor wdColor, Document doc) {
        // separate 1st byte (the most significant) and 3 others to different vars
        int color = ((int) wdColor) & ((int) 0xFFFFFF);
        int colorType = (int) (((uint) wdColor) >> 24);

        if (colorType == RGB) {
            // simple color in OLE format (it's just a BGR - blue, green, red) 
            // let's use standard color translator from system.drawing
            return ColorTranslator.FromOle(color);
        }
        else if (colorType == Automatic) {
            // standard contrast color. In my case I was needed color. But I don't know the proper way to understand which one (black or white) I need to choose.
            return Color.White;
        }
        else if (colorType == System) {
            // In ActiveX controls in documents, and in VBA (for UserForm controls, for example) special values for system colours 
            // (for some reason lost in the mists of time these are also called OLE Colors) ranging from 0x80000000 to 0x80000018. 
            // I used system dll function to retrieve system color and then used standard color translator
            int sysColor = GetSysColor(color);
            return ColorTranslator.FromOle(sysColor);
        }
        else if (colorType >= ThemeLow && colorType <= ThemeHigh) {
            // color based on doc's color theme
            return GetThemedColor(colorType, color, doc);
        }

        throw new Exception("Unknown color type");
    }

    private static Color GetThemedColor(int colorType, int color, Document doc) {
        // color based on theme is base color + tint or shade
        double tintAndShade = 0;
        // base color index is least siginficant 4 bits from colorType
        int colorThemeIndex = colorType & 0xF;

        // 2nd most significant byte is always 0
        // 3rd byte - shade, 4th - tint. One of them must be 0xFF and shouldn't be used
        // it means that always is used one of them and other is 0xFF
        int darkness = (color & 0x00FF00) >> 8;
        int lightness = color & 0x0000FF;

        if (darkness != 0xFF)
            tintAndShade = -1 + darkness / 255.0;
        else
            tintAndShade = 1.0 - lightness / 255.0;
        // so: 
        //      tintAndShade < 0 => shade base color by |tintAndShade| * 100%
        //      tintAndShade > 0 => tint base color |tintAndShade| * 100%

        return GetThemedColor(colorThemeIndex, tintAndShade, doc);
    }

    private static Color GetThemedColor(int colorThemeIndex, double tintAndShade, Document doc) {
        // translate from wdThemeColorIndex to MsoThemeColorSchemeIndex
        MsoThemeColorSchemeIndex colorSchemeIndex = ThemeIndexToSchemeIndex(colorThemeIndex);
        // get color scheme by this index and take its RGB property, but this RGB still OLE RGB - i.e. BGR -> need to convert it to real RGB, i.e. use ColorTranslator.FromOle() and ToArgb after
        OfficeTheme theme = doc.DocumentTheme;
        ThemeColorScheme scheme = theme.ThemeColorScheme;
        ThemeColor color = scheme.Colors(colorSchemeIndex);
        int colorSchemeRGB = ColorTranslator.FromOle(color.RGB).ToArgb();
        Marshal.ReleaseComObject(color);
        Marshal.ReleaseComObject(scheme);
        Marshal.ReleaseComObject(theme);

        // do RGB -> HSL translation to apply tint/shade
        HSL colorSchemeHSL = RGBtoHSL(colorSchemeRGB);

        // apply it
        if (tintAndShade > 0)
            colorSchemeHSL.L += (1 - colorSchemeHSL.L) * tintAndShade;
        else
            colorSchemeHSL.L *= 1 - Math.Abs(tintAndShade);

        // do backward HSL -> RGB translation
        int tintedAndShadedRGB = HSLtoRGB(colorSchemeHSL);

        return Color.FromArgb(tintedAndShadedRGB);
    }

    private static int HSLtoRGB(HSL HSL) {
        // took from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
        double red, green, blue;

        if (HSL.S == 0)
            red = green = blue = HSL.L;
        else {
            double q = HSL.L < 0.5 ? HSL.L * (1 + HSL.S) : HSL.L + HSL.S - HSL.L * HSL.S;
            double p = 2 * HSL.L - q;

            red = Hue2RGB(p, q, HSL.H + 1.0 / 3);
            green = Hue2RGB(p, q, HSL.H);
            blue = Hue2RGB(p, q, HSL.H - 1.0 / 3);
        }

        int r = (int) (red * 255), g = (int) (green * 255), b = (int) (blue * 255);
        return (r << 16) + (g << 8) + b;
    }

    private static double Hue2RGB(double p, double q, double t) {
        // took from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion

        if (t < 0) t += 1;
        if (t > 1) t -= 1;
        if (t < 1.0 / 6) return p + (q - p) * 6 * t;
        if (t < 1.0 / 2) return q;
        if (t < 2.0 / 3) return p + (q - p) * (2.0 / 3 - t) * 6;
        return p;
    }

    private static HSL RGBtoHSL(int RGB) {
        // took from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
        double red, green, blue;
        double max, min, diff;

        red = ((RGB & 0xFF0000) >> 16) / 255.0;
        green = ((RGB & 0x00FF00) >> 8) / 255.0;
        blue = (RGB & 0x0000FF) / 255.0;

        max = Math.Max(red, Math.Max(green, blue));
        min = Math.Min(red, Math.Min(green, blue));
        diff = max - min;

        HSL res;
        res.L = res.H = res.S = (max + min) / 2;
        if (max == min)
            res.S = res.H = 0;
        else {
            res.S = res.L < 0.5 ? diff / (max + min) : diff / (2 - max - min);

            if (red == max)
                res.H = (green - blue) / diff - (blue > green ? 6 : 0);
            else if (green == max)
                res.H = (blue - red) / diff + 2;
            else if (blue == max)
                res.H = (red - green) / diff + 4;
            res.H /= 6;
        }

        return res;
    }

    private static MsoThemeColorSchemeIndex ThemeIndexToSchemeIndex(int colorThemeIndex) {
        // translation sheet from http://www.wordarticles.com/Articles/Colours/2007.php#UIConsiderations
        switch ((WdThemeColorIndex) colorThemeIndex) {
            case WdThemeColorIndex.wdThemeColorMainDark1:
                return MsoThemeColorSchemeIndex.msoThemeDark1;
            case WdThemeColorIndex.wdThemeColorMainLight1:
                return MsoThemeColorSchemeIndex.msoThemeLight1;
            case WdThemeColorIndex.wdThemeColorMainDark2:
                return MsoThemeColorSchemeIndex.msoThemeDark2;
            case WdThemeColorIndex.wdThemeColorMainLight2:
                return MsoThemeColorSchemeIndex.msoThemeLight2;
            case WdThemeColorIndex.wdThemeColorAccent1:
                return MsoThemeColorSchemeIndex.msoThemeAccent1;
            case WdThemeColorIndex.wdThemeColorAccent2:
                return MsoThemeColorSchemeIndex.msoThemeAccent2;
            case WdThemeColorIndex.wdThemeColorAccent3:
                return MsoThemeColorSchemeIndex.msoThemeAccent3;
            case WdThemeColorIndex.wdThemeColorAccent4:
                return MsoThemeColorSchemeIndex.msoThemeAccent4;
            case WdThemeColorIndex.wdThemeColorAccent5:
                return MsoThemeColorSchemeIndex.msoThemeAccent5;
            case WdThemeColorIndex.wdThemeColorAccent6:
                return MsoThemeColorSchemeIndex.msoThemeAccent6;
            case WdThemeColorIndex.wdThemeColorHyperlink:
                return MsoThemeColorSchemeIndex.msoThemeHyperlink;
            case WdThemeColorIndex.wdThemeColorHyperlinkFollowed:
                return MsoThemeColorSchemeIndex.msoThemeFollowedHyperlink;
            case WdThemeColorIndex.wdThemeColorBackground1:
                return MsoThemeColorSchemeIndex.msoThemeLight1;
            case WdThemeColorIndex.wdThemeColorText1:
                return MsoThemeColorSchemeIndex.msoThemeDark1;
            case WdThemeColorIndex.wdThemeColorBackground2:
                return MsoThemeColorSchemeIndex.msoThemeLight2;
            case WdThemeColorIndex.wdThemeColorText2:
                return MsoThemeColorSchemeIndex.msoThemeDark2;
            default:
                throw new Exception("Unknown WdThemeColorIndex: " + colorThemeIndex);
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetSysColor(int nIndex);
}

The following image illustrates the problems of directly passing the value of interop Font.Color to ColorTranslator.FromOle . 下图说明了将interop Font.Color的值直接传递给ColorTranslator.FromOle Also, it shows that there is some kind of color blending that happens when requesting color information by each word Range . 同样,它表明当按每个单词Range请求颜色信息时,会发生某种颜色混合。

颜色错误的示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将整个datagridview导出成word文档(在C#中使用office word interop DLL) - How to export the entire datagridview into a word document (using office word interop DLL in C#) 如何使用Microsoft.Office.Interop.Word C#在Word文档中插入/获取封面 - How to insert/fetch a cover page in word document using Microsoft.Office.Interop.Word C# 确定是否选中了word文档中的复选框 - c#中的char u0015、microsoft.office.interop.word - Determine if checkbox in word document is checked or not - char u0015, microsoft.office.interop.word in c# 如何在不使用microsoft.office.interop的情况下将Word文档转换为C#中的文本文件? - How to convert a word document to a text file in c# without using microsoft.office.interop? 如何使用c#Word Interop清除MS Office剪贴板 - How to clear the MS Office Clipboard using c# Word Interop 使用C#中的Microsoft.Office.Interop.Word在MS Word中查找具有指定字体的文本 - Finding Text With specified font in MS word using Microsoft.Office.Interop.Word in c# 如何在没有office.word.interop C#的情况下将带有图表的MS Word文档转换为PDF - How to convert MS word document with chart in it to PDF without office.word.interop c# C#Microsoft.Office.Interop.Word.Document流 - c# Microsoft.Office.Interop.Word.Document to Stream 使用Interop C#格式化Word文档 - Formatting a Word document using interop c# 如何使用C#和Office Interop获取Office文档的完整文件名? - How do I get the full filename for an Office document using C# and Office Interop?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM