[英]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> </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.