¿Cómo sé el tamaño de fuente (por ejemplo) de un texto específico en un documento de Word?

La idea es simple, pero la respuesta puede complicarse:

De hecho, puedo verificar las propiedades de ejecución para el tamaño de fuente.

Si está ausente, necesito verificar el estilo aplicado al párrafo para encontrar las propiedades de ejecución definidas para el tamaño de fuente, luego las propiedades de ejecución del párrafo de ese estilo.

Si no lo encuentra, necesito verificar todo nuevamente con respecto al estilo en el que se basa este estilo.

Si no lo encuentra, debo verificar el siguiente estilo subiendo en la jerarquía de estilos y continuar hasta llegar al estilo predeterminado.

También necesito verificar si el párrafo anterior tiene un estilo aplicado. En este caso, el estilo aplicado puede definir el estilo del siguiente párrafo que afecta el texto con el que estoy trabajando.

Si no hay ningún estilo que influya en mi párrafo, entonces debo buscar en las propiedades de ejecución predeterminadas de la parte de estilos. Después de eso, debería buscar las propiedades de párrafo predeterminadas en la misma parte.

Si no se aplica nada, la responsabilidad de la definición del tamaño recae en la aplicación que está trabajando con el documento.

¿Estoy en lo cierto?

¿No tengo ayuda de OPenXML SDK y/o de OpenXmlPowerTools?

Un aspecto importante es que esta pregunta se extiende a casi cualquier párrafo o propiedad de ejecución además del tamaño de fuente del texto.

Mi objetivo final es averiguar si un fragmento de texto es un encabezado de sección (como el encabezado 1, el encabezado 2, etc.) en función del formato, pero parece difícil obtener algo tan simple como "el formato actual de un fragmento de texto". Para hacer las cosas más difíciles, también necesito lidiar con la numeración (sección) que muchas veces no tiene un formato de numeración aplicado al párrafo.

Gracias,

preguntado el 03 de enero de 13 a las 19:01

¿Se requiere que esto se resuelva usando openxml-sdk? ¿O sería aplicable una solución basada en Word Interop API? -

No se debe usar Word Interop-API. -

Tengo una respuesta parcial de Eric White ( ericwhite.com ): "Por el momento, no hay buenos ejemplos de esto ni en Open XML SDK ni en PowerTools". Entonces, tengo que trabajar en la solución. Puede tomar el mismo tiempo. Después de eso publicaré los resultados. Gracias -

1 Respuestas

Entonces, estoy respondiendo mi propia pregunta como prometí.

Desarrollé un método que devuelve las propiedades de ejecución "efectivas" de una ejecución específica de un párrafo de documento de Word. Tiene en cuenta las propiedades predeterminadas del documento, los estilos aplicados, incluida la jerarquía de estilo relacionada, y las propiedades de ejecución directa de acuerdo con la norma ISO/IEC29500-1.

Es interesante notar que Word no parece seguir completamente el estándar en estos dos aspectos: 1 - Si un párrafo no tiene estilo aplicado, Word aplica el estilo de párrafo predeterminado. Que yo sepa creo que no se debe aplicar ningún estilo. Esto no sucede con una ejecución: cuando una ejecución no tiene un estilo de ejecución, no se aplica el estilo de ejecución predeterminado. 2 - Para obtener las propiedades de ejecución efectivas, es necesario "resumir estilos". Los estilos de párrafo y los estilos de ejecución siguen una jerarquía de estilo. Para obtener un valor de propiedad específico, es necesario buscarlo en el estilo aplicado, si no está presente, buscarlo en el estilo principal y así sucesivamente. Una propiedad que se define con un valor específico en un estilo determinado no debe agregarse al estilo secundario si tiene el mismo valor. Word no sigue esta regla para los estilos de carácter. De hecho, todas las propiedades de ejecución aplicadas desde el estilo de ejecución se pueden obtener directamente para ese estilo de ejecución sin que sea necesario seguir la jerarquía de estilos. Esto no está de acuerdo con el estándar.

Ahora, déjame entrar en algunos detalles de mi solución:

Primero, mi código usa las herramientas eléctricas de openxml: http://powertools.codeplex.com/

A continuación, para incorporar estilos con respecto a la herencia de estilos, adapté e implementé la solución proporcionada por Eric White en: http://blogs.msdn.com/b/ericwhite/archive/2009/12/13/implementing-inheritance-in-xml.aspx y http://blogs.msdn.com/b/ericwhite/archive/2009/10/29/open-xml-wordprocessingml-style-inheritance.aspx

El algoritmo completo para obtener las propiedades de ejecución se puede encontrar en el estándar y también lo proporciona Eric White en: http://blogs.msdn.com/b/ericwhite/archive/2009/11/12/assembling-paragraph-and-run-properties-for-cells-in-a-table.aspx En este caso se trata de la extracción de propiedades de una celda dentro de una tabla. Mi método no funciona para párrafos dentro de tablas (simplemente no lo necesito :-)), pero puede extenderse para tratar estos casos (toda la información está en el artículo de Eric)

Tenga en cuenta que trato correctamente las propiedades de alternancia y la forma en que Word realmente funciona (los puntos que hice para las diferencias relacionadas con el estándar.

Finalmente, el código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using OpenXmlPowerTools;

namespace MyNameSpace
{
    class OpenXmlPowerToolsUtilities
    {
        public static XElement GetEffectiveRunProperties(WordprocessingDocument wordDoc, XElement run)
        {
            XElement runProperties = null;
            List<XElement> runPropertiesList = new List<XElement>();

            XElement paragraph = run.Parent;
            if (paragraph.Name != W.p)
                return null;

            StyleDefinitionsPart styleDefinitionsPart = wordDoc.MainDocumentPart
                .StyleDefinitionsPart;
            if (styleDefinitionsPart == null)
                return null;
            XElement styles = styleDefinitionsPart.GetXDocument().Root;           

            // 1 - Get run default
            XElement runDefault = styles.Elements(W.docDefaults)
                .Elements(W.rPrDefault)
                .Elements(W.rPr)
                .FirstOrDefault();
            if (runDefault != null)
                runPropertiesList.Add(runDefault);

            // 2 - get paragraph style run properties
            XElement pStyleRunProperties = null;
            string pStyle = (string)paragraph.Elements(W.pPr)
                .Elements(W.pStyle)
                .Attributes(W.val)
                .FirstOrDefault();
            if (pStyle != null)
            {
                pStyleRunProperties = AssembleStyleInformation(styles, pStyle)
                                                       .Elements(W.rPr)
                                                       .FirstOrDefault();
            }
            else
            {
                XElement defaultParagraphStyle = styles
                                        .Elements(W.style)
                                        .Where(e =>
                                            (string)e.Attribute(W.type) == "paragraph" &&
                                               (string)e.Attribute(W._default) == "1")
                                        .Select(s => s)
                                        .FirstOrDefault();
                pStyleRunProperties = defaultParagraphStyle.Elements(W.rPr).FirstOrDefault();
            }
            if (pStyleRunProperties != null)
                runPropertiesList.Add(pStyleRunProperties);

            // 3 - get run style run properties
            string rStyle = (string)run.Elements(W.rPr).Elements(W.rStyle).Attributes(W.val).FirstOrDefault();
            XElement rStyleRunProperties = null;
            if (rStyle != null)
            {
                rStyleRunProperties = AssembleStyleInformation(styles, rStyle)
                                                       .Elements(W.rPr)
                                                       .FirstOrDefault();
            }
            if (rStyleRunProperties != null)
                runPropertiesList.Add(rStyleRunProperties);

            XElement toggleProperties = AssembleToggleProperties(runDefault, pStyleRunProperties, rStyleRunProperties);
            if (toggleProperties != null)
                runPropertiesList.Add(toggleProperties);

            // 4 - direct run properties
            XElement directRunProperties = run.Elements(W.rPr).FirstOrDefault();
            if (directRunProperties != null)
                runPropertiesList.Add(directRunProperties);

            runProperties = AssembleRunProperties(runPropertiesList);

            return runProperties;
        }

        private static XElement AssembleRunProperties(List<XElement> runPropertiesList)
        {
            return runPropertiesList
                .Aggregate(
                    new XElement(W.rPr,
                    new XAttribute(XNamespace.Xmlns + "w", W.w)),
                    (mergedRun, run) =>
                        MergeChildElements(mergedRun, run));
        }

        static XElement AssembleToggleProperties(XElement runDefault, XElement pStyleRunProperties, XElement rStyleRunProperties)
        {
            XElement runToggleProperties;

            runToggleProperties = new XElement(W.rPr, 
                new XAttribute(XNamespace.Xmlns + "w", W.w));

            foreach (XName toggleProperty in toggleProperties)
            {
                XElement runDefaultToggleProperty = runDefault.Elements(toggleProperty).FirstOrDefault();

                if (runDefaultToggleProperty != null)
                {
                    if ((string)runDefaultToggleProperty.Attributes(W.val).FirstOrDefault() != "0")
                    {
                        runToggleProperties.Add(runDefaultToggleProperty);
                        continue;
                    }
                }

                XElement pStyleToggleProperty = null;
                if (pStyleRunProperties == null)
                    pStyleToggleProperty = null;
                else
                    pStyleToggleProperty = pStyleRunProperties.Elements(toggleProperty).FirstOrDefault();

                XElement rStyleToggleProperty = null;
                if (rStyleRunProperties == null)
                    rStyleToggleProperty = null;
                else
                    rStyleToggleProperty = rStyleRunProperties.Elements(toggleProperty).FirstOrDefault();

                if (pStyleToggleProperty == null && rStyleToggleProperty != null)
                    runToggleProperties.Add(rStyleToggleProperty);
                else if (pStyleToggleProperty != null && rStyleToggleProperty == null)
                    runToggleProperties.Add(pStyleToggleProperty);
                else if (pStyleToggleProperty != null && rStyleToggleProperty != null)
                {
                    if ((string)rStyleToggleProperty.Attributes(W.val).FirstOrDefault() == "0")
                        runToggleProperties.Add(pStyleToggleProperty);
                    else if ((string)pStyleToggleProperty.Attributes(W.val).FirstOrDefault() == "0")
                        runToggleProperties.Add(rStyleToggleProperty);
                    else
                        runToggleProperties.Add(new XElement(toggleProperty, new XAttribute(W.val, "0")));
                }
            }

            return runToggleProperties;
        }

        public static IEnumerable<XElement> StyleChainReverseOrder(XElement styles, string styleId)
        {
            string current = styleId;
            while (true)
            {
                XElement style = styles.Elements(W.style)
                    .Where(s => (string)s.Attribute(W.styleId) == current).FirstOrDefault();
                yield return style;
                current = (string)style.Elements(W.basedOn).Attributes(W.val).FirstOrDefault();
                if (current == null)
                    yield break;
            }
        }

        public static IEnumerable<XElement> StyleChain(XElement styles, string styleId)
        {
            return StyleChainReverseOrder(styles, styleId).Reverse();
        }

        private static XElement AssembleStyleInformation(XElement styles, string styleId)
        {
            return StyleChain(styles, styleId)
                .Aggregate(
                    new XElement(W.style, new XAttribute(XNamespace.Xmlns + "w", W.w)),
                    (mergedStyle, style) => MergeChildElements(mergedStyle, style));
        }

        public static XName[] Others =
        {
            W.pStyle,
            W.rStyle
        };

        public static XName[] ElementsWithMergeElementsSemantics =
        {
            W.style,
            W.rPr,
            W.pPr 
        };

        public static XName[] ElementsWithMergeAttributesSemantics =
        {
            W.ind,
            W.spacing,
            W.lang
        };

        public static XName[] ElementsWithReplaceElementsSemantics =
        {
            W.name, // The style Name element
            W.adjustRightInd,
            W.autoSpaceDE,
            W.autoSpaceDN,
            W.bidi,
            W.cnfStyle,  // within a table
            W.contextualSpacing,
            W.divId, 
            W.framePr,
            W.jc,
            W.keepLines,
            W.keepNext,
            W.kinsoku,
            W.mirrorIndents,
            W.numPr,
            W.outlineLvl,
            W.overflowPunct,
            W.pageBreakBefore,
            W.pBdr,  
            W.shd,
            W.snapToGrid,
            W.suppressAutoHyphens,
            W.suppressLineNumbers,
            W.suppressOverlap,
            W.tabs,  
            W.textAlignment,
            W.textboxTightWrap,  // within a textbox
            W.textDirection,
            W.topLinePunct,
            W.widowControl,
            W.wordWrap,
            W.b,
            W.bCs,
            W.bdr,
            W.caps,
            W.color,
            W.cs,
            W.dstrike,
            W.eastAsianLayout,
            W.effect,
            W.em,
            W.emboss,
            W.fitText,
            W.highlight,
            W.i,
            W.iCs,
            W.imprint,
            W.kern,
            W.noProof,
            W.oMath,
            W.outline,
            W.position,
            W.rFonts,
            W.rtl,
            W.shadow,
            W.shd,
            W.smallCaps,
            W.snapToGrid,
            //W.spacing,   // different from paragraph spacing
            W.specVanish,
            W.strike,
            W.sz,
            W.szCs,
            W.u,  
            W.vanish,
            W.vertAlign,
            W._w, 
            W.webHidden           
        };

        public static XName[] toggleProperties =
        {
            W.b,
            W.bCs,
            W.caps,
            W.emboss,
            W.i,
            W.iCs,
            W.imprint,
            W.outline,
            W.shadow,
            W.smallCaps,
            W.strike,
            W.vanish
        };

        public static bool IsValidMergeElement(XName name)
        {
            if (ElementsWithMergeAttributesSemantics.Contains(name) ||
                ElementsWithMergeElementsSemantics.Contains(name) ||
                ElementsWithReplaceElementsSemantics.Contains(name))
                return true;

            return false;
        }

        public static bool IsToggleProperty(XName name)
        {
            if (toggleProperties.Contains(name))
                return true;

            return false;
        }

        public static bool HasReplaceSemantics(XName name)
        {
            if (ElementsWithReplaceElementsSemantics.Contains(name))
                return true;

            return false;
        }

        public static bool HasMergeElementsSemantics(XName name)
        {
            if (ElementsWithMergeElementsSemantics.Contains(name))
                return true;

            return false;
        }

        public static bool HasMergeAttributesSemantics(XName name)
        {
            if (ElementsWithMergeAttributesSemantics.Contains(name))
                return true;

            return false;
        }

        public static XElement MergeChildElements(XElement mergedElement, XElement element)
        {
            if (mergedElement == null || element == null)
            {
                if (element == null)
                    element = mergedElement;
                XElement newElement = new XElement(element.Name,
                    new XAttribute(XNamespace.Xmlns + "w", W.w),
                    element.Attributes()
                   .Where(a =>
                   {
                       if (a.IsNamespaceDeclaration)
                           return false;
                       if (element.Name == W.style)
                           if (!(a.Name == W.type || a.Name == W.styleId))
                               return false;
                       return true;
                   }),
                element.Elements().Select(e =>
                {
                    if (e.Name == W.rPr || e.Name == W.pPr)
                        return MergeChildElements(null, e);

                    if (IsValidMergeElement(e.Name))
                        return e;

                    return null;
                }));
                return newElement;
            }

            XElement newMergedElement = new XElement(element.Name,
                new XAttribute(XNamespace.Xmlns + "w", W.w),
                element.Attributes()
                   .Where(a =>
                   {
                       if (a.IsNamespaceDeclaration)
                           return false;
                       if (element.Name == W.style)
                           if (!(a.Name == W.type || a.Name == W.styleId))
                               return false;
                       return true;
                   }),
                element.Elements().Select(e =>
                {
                    if (HasReplaceSemantics(e.Name))
                        return e;

                    // spacing within run properties has replace semantics 
                    if (element.Name == W.rPr && e.Name == W.spacing)
                        return e;

                    if (HasMergeAttributesSemantics(e.Name))
                    {
                        XElement newElement;
                        newElement = new XElement(e.Name,
                            e.Attributes(),
                            mergedElement.Elements(e.Name).Attributes()
                                .Where(a =>
                                    !(e.Attributes().Any(z => z.Name == a.Name))));
                        return newElement;
                    }

                    if (e.Name == W.rPr || e.Name == W.pPr)
                    {
                        XElement correspondingElement = mergedElement.Element(e.Name);
                        return MergeChildElements(correspondingElement, e);
                    }
                    return null;
                }),
                mergedElement.Elements()
                    .Where(m => !element.Elements(m.Name).Any()));

            return newMergedElement;
        }
    }
}

contestado el 06 de mayo de 13 a las 19:05

¿Puedo usar un enfoque similar para PowerPoint? Estoy tratando de configurar el tipo de fuente para todas las ejecuciones que no tienen un conjunto. Creo que necesito seguir el proceso de selección de fuentes. blogs.msdn.com/b/officeinteroperability/archive/2013/04/22/… - pablo

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.