Esempio n. 1
0
 internal int SubpageShiftVertical(
     IntPtr pfspara,                     // IN:  pointer to the para data
     IntPtr pfsparaclient,               // IN:
     IntPtr pfsshift,                    // IN:  pointer to the shift data
     uint fswdir,                        // IN:  wdir for bbox - the same as the one passed to formatting method
     out PTS.FSBBOX pfsbbox)             // OUT: output BBox
 {
     Debug.Assert(false);
     pfsbbox = new PTS.FSBBOX();
     return PTS.fserrNone;
 }
Esempio n. 2
0
        private PTS.FSBBOX GetBoundingBox()
        { 
            PTS.FSBBOX bbox = new PTS.FSBBOX();
            // There are 3 cases when calculating bounding box:
            // (1) PTS page is not created - return empty rect.
            // (2) PTS page - use page PTS APIs to get bounding box.
            // (3) PTS subpage - use subpage PTS APIs to get bounding box.
            if (IsEmpty)
            {
                // (1) PTS page is not created - bbox is not defined
            }
            else
            {
                // (2) PTS page - use page PTS APIs to get bounding box.
                PTS.FSPAGEDETAILS pageDetails;
                PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));

                // There are 2 different types of PTS page and bounding box calculation depends on it:
                // (a) simple page (contains only one track) - get bounding box from the track.
                // (b) complex page (contains header, page body, footnotes and footer) - get bounding 
                //     box of each segment and union them.
                if (PTS.ToBoolean(pageDetails.fSimple))
                {
                    // (a) simple page (contains only one track) - get bounding box from the track.
                    bbox = pageDetails.u.simple.trackdescr.fsbbox;
                }
                else
                {
                    // (b) complex page (contains header, page body, footnotes and footer) - get bounding 
                    //     box of each segment and union them.
                    //ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
                    //ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
                    ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
                    // Since header/footer and footnotes are not supported yet, use page body bounding box
                    bbox = pageDetails.u.complex.fsbboxPageBody;
                }
            }
            return bbox;
        }
Esempio n. 3
0
        internal int SubpageFormatParaFinite(
            IntPtr pfssobjc,                    // IN:  object context
            IntPtr pfsparaclient,               // IN:
            IntPtr pfsobjbrk,                   // IN:  break record---use if !NULL
            int fBreakRecordFromPreviousPage,   // IN:  break record was created on previous page
            IntPtr nmp,                         // IN:  name of paragraph---use if break record is NULL
            int iArea,                          // IN:  column-span area index
            IntPtr pftnrej,                     // IN:
            IntPtr pfsgeom,                     // IN:  pointer to geometry
            int fEmptyOk,                       // IN:  is it OK not to add anything?
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  current direction
            ref PTS.FSRECT fsrcToFill,          // IN:  rectangle to fill
            IntPtr pmcsclientIn,                // IN:  input margin collapsing state
            PTS.FSKCLEAR fskclearIn,            // IN:  clear property that must be satisfied
            PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn,

            int fBreakInside,                   // IN:  produce vertical break inside para; needed for recursive KWN logic;

            out PTS.FSFMTR fsfmtr,              // OUT: result of formatting the paragraph
            out IntPtr pfspara,                 // OUT: pointer to the para data
            out IntPtr pbrkrecpara,             // OUT: pointer to the para break record
            out int dvrUsed,                    // OUT: vertical space used by the para
            out PTS.FSBBOX fsbbox,              // OUT: para BBox
            out IntPtr pmcsclientOut,           // OUT: margin collapsing state at the bottom
            out PTS.FSKCLEAR fskclearOut,       // OUT: ClearIn for the next paragraph
            out int dvrTopSpace,                // OUT: top space due to collapsed margin
            out int fBreakInsidePossible)       // OUT: internal vertical break possible, needed for recursive KWN logic
        {
            int fserr = PTS.fserrNone;
            fBreakInsidePossible = PTS.False;
            try
            {
                SubpageParagraph para = PtsContext.HandleToObject(nmp) as SubpageParagraph;
                PTS.ValidateHandle(para);
                SubpageParaClient paraClient = PtsContext.HandleToObject(pfsparaclient) as SubpageParaClient;
                PTS.ValidateHandle(paraClient);
                MarginCollapsingState mcs = null;
                if (pmcsclientIn != IntPtr.Zero)
                {
                    mcs = PtsContext.HandleToObject(pmcsclientIn) as MarginCollapsingState;
                    PTS.ValidateHandle(mcs);
                }
                para.FormatParaFinite(paraClient, pfsobjbrk, fBreakRecordFromPreviousPage, pftnrej, 
                    fEmptyOk, fSuppressTopSpace, fswdir, ref fsrcToFill, mcs, fskclearIn, fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfspara, 
                    out pbrkrecpara, out dvrUsed, out fsbbox, out pmcsclientOut, out fskclearOut, out dvrTopSpace);
            }
            catch (Exception e)
            {
                fsfmtr = new PTS.FSFMTR(); pfspara = pbrkrecpara = pmcsclientOut = IntPtr.Zero; dvrUsed = dvrTopSpace = 0; fsbbox = new PTS.FSBBOX(); 
                fskclearOut = default(PTS.FSKCLEAR);
                PtsContext.CallbackException = e;
                fserr = PTS.fserrCallbackException;
            }
            catch
            {
                fsfmtr = new PTS.FSFMTR(); pfspara = pbrkrecpara = pmcsclientOut = IntPtr.Zero; dvrUsed = dvrTopSpace = 0; fsbbox = new PTS.FSBBOX(); 
                fskclearOut = default(PTS.FSKCLEAR);
                PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception");
                fserr = PTS.fserrCallbackException;
            }
            return fserr;
        }
Esempio n. 4
0
 internal int SubpageUpdateBottomlessPara(
     IntPtr pfspara,                     // IN:  pointer to the para data
     IntPtr pfsparaclient,               // IN:
     IntPtr nmp,                         // IN:  name of paragraph
     int iArea,                          // IN:  column-span area index
     IntPtr pfsgeom,                     // IN:  pointer to geometry
     int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
     uint fswdir,                        // IN:  current direction
     int urTrack,                        // IN:  u of bootomless rectangle to fill
     int durTrack,                       // IN:  du of bootomless rectangle to fill
     int vrTrack,                        // IN:  v of bootomless rectangle to fill
     IntPtr pmcsclientIn,                // IN:  input margin collapsing state
     PTS.FSKCLEAR fskclearIn,            // IN:  clear property that must be satisfied
     int fInterruptable,                 // IN:  formatting can be interrupted
     out PTS.FSFMTRBL fsfmtrbl,          // OUT: result of formatting the paragraph
     out int dvrUsed,                    // OUT: vertical space used by the para
     out PTS.FSBBOX fsbbox,              // OUT: para BBox
     out IntPtr pmcsclientOut,           // OUT: margin collapsing state at the bottom
     out PTS.FSKCLEAR fskclearOut,       // OUT: ClearIn for the next paragraph
     out int dvrTopSpace,                // OUT: top space due to collapsed margin
     out int fPageBecomesUninterruptable)// OUT: interruption is prohibited from now on
 {
     int fserr = PTS.fserrNone;
     try
     {
         SubpageParagraph para = PtsContext.HandleToObject(nmp) as SubpageParagraph;
         PTS.ValidateHandle(para);
         SubpageParaClient paraClient = PtsContext.HandleToObject(pfsparaclient) as SubpageParaClient;
         PTS.ValidateHandle(paraClient);
         MarginCollapsingState mcs = null;
         if (pmcsclientIn != IntPtr.Zero)
         {
             mcs = PtsContext.HandleToObject(pmcsclientIn) as MarginCollapsingState;
             PTS.ValidateHandle(mcs);
         }
         para.UpdateBottomlessPara(pfspara, paraClient, fSuppressTopSpace, fswdir, urTrack, durTrack, 
             vrTrack, mcs, fskclearIn, fInterruptable, out fsfmtrbl, out dvrUsed, out fsbbox, 
             out pmcsclientOut, out fskclearOut, out dvrTopSpace, out fPageBecomesUninterruptable);
     }
     catch (Exception e)
     {
         fsfmtrbl = default(PTS.FSFMTRBL); 
         pfspara = pmcsclientOut = IntPtr.Zero; dvrUsed = dvrTopSpace = fPageBecomesUninterruptable = 0; fsbbox = new PTS.FSBBOX(); 
         fskclearOut = default(PTS.FSKCLEAR);
         PtsContext.CallbackException = e;
         fserr = PTS.fserrCallbackException;
     }
     catch
     {
         fsfmtrbl = default(PTS.FSFMTRBL); 
         pfspara = pmcsclientOut = IntPtr.Zero; dvrUsed = dvrTopSpace = fPageBecomesUninterruptable = 0; fsbbox = new PTS.FSBBOX(); 
         fskclearOut = default(PTS.FSKCLEAR);
         PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception");
         fserr = PTS.fserrCallbackException;
     }
     return fserr;
 }
Esempio n. 5
0
        internal int CalcFigurePosition(
            IntPtr pfsclient,                   // IN:  client opaque data
            IntPtr pfsparaclientFigure,         // IN:
            IntPtr nmpFigure,                   // IN:  figure's name
            uint fswdir,                        // IN:  current direction
            ref PTS.FSRECT fsrcPage,            // IN:  page rectangle
            ref PTS.FSRECT fsrcMargin,          // IN:  rectangle within page margins
            ref PTS.FSRECT fsrcTrack,           // IN:  track rectangle
            ref PTS.FSRECT fsrcFigurePreliminary,// IN:  prelim figure rect calculated from figure props
            int fMustPosition,                  // IN:  must find position in this track?
            int fInTextLine,                    // IN:  it is attached to text line
            out int fPushToNextTrack,           // OUT: push to next track?
            out PTS.FSRECT fsrcFlow,            // OUT: FlowAround rectangle
            out PTS.FSRECT fsrcOverlap,         // OUT: Overlap rectangle
            out PTS.FSBBOX fsbbox,              // OUT: bbox
            out PTS.FSRECT fsrcSearch)          // OUT: search area for overlap
        {
            int fserr = PTS.fserrNone;
            try
            {
                FigureParagraph para = PtsContext.HandleToObject(nmpFigure) as FigureParagraph;
                PTS.ValidateHandle(para);
                FigureParaClient paraClient = PtsContext.HandleToObject(pfsparaclientFigure) as FigureParaClient;
                PTS.ValidateHandle(paraClient);
                para.CalcFigurePosition(paraClient, fswdir, ref fsrcPage, ref fsrcMargin, ref fsrcTrack, 
                    ref fsrcFigurePreliminary, fMustPosition, fInTextLine, out fPushToNextTrack, out fsrcFlow, 
                    out fsrcOverlap, out fsbbox, out fsrcSearch);
            }
            catch (Exception e)
            {
                fPushToNextTrack = 0; fsrcFlow = fsrcOverlap = fsrcSearch = new PTS.FSRECT(); fsbbox = new PTS.FSBBOX();
                PtsContext.CallbackException = e;
                fserr = PTS.fserrCallbackException;
            }
            catch
            {
                fPushToNextTrack = 0; fsrcFlow = fsrcOverlap = fsrcSearch = new PTS.FSRECT(); fsbbox = new PTS.FSBBOX();
                PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception");
                fserr = PTS.fserrCallbackException;
            }

            return fserr;
        }
Esempio n. 6
0
 internal int UpdateBottomlessFloaterContent(
     IntPtr pfsFloaterContent,           // IN:  opaque for PTS pointer to floater content
     IntPtr pfsparaclient,               // IN:
     IntPtr nmFloater,                   // IN:  name of floater
     int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
     uint fswdirTrack,                   // IN:  direction of Track
     int fAtMaxWidth,                    // IN:  formating is at full width of column
     int durAvailable,                   // IN:  width of available space
     int dvrAvailable,                   // IN:  height of available space
     out PTS.FSFMTRBL fsfmtrbl,          // OUT: result of formatting
     out int durFloaterWidth,            // OUT: floater width
     out int dvrFloaterHeight,           // OUT: floater height
     out PTS.FSBBOX fsbbox,              // OUT: floater bbox
     out int cPolygons,                  // OUT: number of polygons
     out int cVertices)                  // OUT: total number of vertices in all polygons
 {
     int fserr = PTS.fserrNone;
     try
     {
         FloaterBaseParagraph para = PtsContext.HandleToObject(nmFloater) as FloaterBaseParagraph;
         PTS.ValidateHandle(para);
         FloaterBaseParaClient paraClient = PtsContext.HandleToObject(pfsparaclient) as FloaterBaseParaClient;
         PTS.ValidateHandle(paraClient);
         para.UpdateBottomlessFloaterContent(paraClient, fSuppressTopSpace, fswdirTrack, fAtMaxWidth, 
             durAvailable, dvrAvailable, pfsFloaterContent, out fsfmtrbl, out durFloaterWidth, out dvrFloaterHeight,
             out fsbbox, out cPolygons, out cVertices);
     }
     catch (Exception e)
     {
         fsfmtrbl = default(PTS.FSFMTRBL); 
         durFloaterWidth = dvrFloaterHeight = cPolygons = cVertices = 0; fsbbox = new PTS.FSBBOX();
         PtsContext.CallbackException = e;
         fserr = PTS.fserrCallbackException;
     }
     catch
     {
         fsfmtrbl = default(PTS.FSFMTRBL); 
         durFloaterWidth = dvrFloaterHeight = cPolygons = cVertices = 0; fsbbox = new PTS.FSBBOX();
         PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception");
         fserr = PTS.fserrCallbackException;
     }
     return fserr;
 }
        //-------------------------------------------------------------------
        // CalcFigurePosition
        //-------------------------------------------------------------------
        internal void CalcFigurePosition(
            FigureParaClient paraClient,        // IN:
            uint fswdir,                        // IN:  current direction
            ref PTS.FSRECT fsrcPage,            // IN:  page rectangle
            ref PTS.FSRECT fsrcMargin,          // IN:  rectangle within page margins
            ref PTS.FSRECT fsrcTrack,           // IN:  track rectangle
            ref PTS.FSRECT fsrcFigurePreliminary,// IN:  prelim figure rect calculated from figure props
            int fMustPosition,                  // IN:  must find position in this track?
            int fInTextLine,                    // IN:  it is attached to text line
            out int fPushToNextTrack,           // OUT: push to next track?
            out PTS.FSRECT fsrcFlow,            // OUT: FlowAround rectangle
            out PTS.FSRECT fsrcOverlap,         // OUT: Overlap rectangle
            out PTS.FSBBOX fsbbox,              // OUT: bbox
            out PTS.FSRECT fsrcSearch)          // OUT: search area for overlap
        {
            Figure element = (Figure)Element;

            // If overlapping happens, let PTS find another position withing
            // the track rectangle.

            FigureHorizontalAnchor horizAnchor = element.HorizontalAnchor;
            FigureVerticalAnchor vertAnchor = element.VerticalAnchor;

            fsrcSearch = CalculateSearchArea(horizAnchor, vertAnchor, ref fsrcPage, ref fsrcMargin, ref fsrcTrack, ref fsrcFigurePreliminary);

            if(vertAnchor == FigureVerticalAnchor.ParagraphTop && 
               fsrcFigurePreliminary.v != fsrcMargin.v && // If we're not at the top of the column
               ( (fsrcFigurePreliminary.v + fsrcFigurePreliminary.dv) > (fsrcTrack.v + fsrcTrack.dv) ) && // And we exceed column height
               !PTS.ToBoolean(fMustPosition)) // Can delay placement is handled by figure properties.
            {
                fPushToNextTrack = PTS.True;
            }
            else
            {
                fPushToNextTrack = PTS.False;
            }


            // Use rectangle proposed by PTS and make sure that figure fits completely in the page.

            fsrcFlow = fsrcFigurePreliminary;

            if(FigureHelper.IsHorizontalColumnAnchor(horizAnchor))
            {
                fsrcFlow.u += CalculateParagraphToColumnOffset(horizAnchor, fsrcFigurePreliminary);
            }
            
            // Apply horizontal and vertical offsets. Offsets are limited by page height and width
            fsrcFlow.u += TextDpi.ToTextDpi(element.HorizontalOffset);
            fsrcFlow.v += TextDpi.ToTextDpi(element.VerticalOffset);

            // Overlap rectangle is the same as flow around rect
            fsrcOverlap = fsrcFlow;


            /* If we're anchored to column/content left or right, inflate our overlap width to prevent from aligning two figures right next to one another
            by incorporating column gap information */
            if(!FigureHelper.IsHorizontalPageAnchor(horizAnchor) && 
               horizAnchor != FigureHorizontalAnchor.ColumnCenter &&
               horizAnchor != FigureHorizontalAnchor.ContentCenter)
            {
                double columnWidth, gap, rule;
                int cColumns;

                FigureHelper.GetColumnMetrics(StructuralCache, out cColumns, out columnWidth, out gap, out rule);

                int duColumnWidth = TextDpi.ToTextDpi(columnWidth);
                int duGapWidth = TextDpi.ToTextDpi(gap);
                int duColumnWidthWithGap = duColumnWidth + duGapWidth;
                int fullColumns = (fsrcOverlap.du / duColumnWidthWithGap);
                int duRoundedToNearestColumn = ((fullColumns + 1) * duColumnWidthWithGap) - duGapWidth;

                fsrcOverlap.du = duRoundedToNearestColumn; // Round overlap rect to nearest column

                if(horizAnchor == FigureHorizontalAnchor.ContentRight || 
                   horizAnchor == FigureHorizontalAnchor.ColumnRight)
                {
                    fsrcOverlap.u = (fsrcFlow.u + fsrcFlow.du + duGapWidth) - fsrcOverlap.du;
                }

                // Force search rect to only work vertically within overlap space.
                fsrcSearch.u = fsrcOverlap.u;
                fsrcSearch.du = fsrcOverlap.du;
            }

            // Bounding box is equal to actual size of the figure.
            fsbbox = new PTS.FSBBOX();
            fsbbox.fDefined = PTS.True;
            fsbbox.fsrc = fsrcFlow;
        }
Esempio n. 8
0
        internal int FormatFloaterContentFinite(
            IntPtr pfsclient,                   // IN:  client opaque data
            IntPtr pfsparaclient,               // IN:
            IntPtr pfsbrkFloaterContentIn,      // IN:  break record---use if !NULL
            int fBreakRecordFromPreviousPage,   // IN:  break record was created on previous page
            IntPtr nmFloater,                   // IN:  name of floater
            IntPtr pftnrej,                     // IN: 
            int fEmptyOk,                       // IN:  is it OK not to add anything?
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdirTrack,                   // IN:  direction of Track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn,

            out PTS.FSFMTR fsfmtr,              // OUT: result of formatting
            out IntPtr pfsFloatContent,         // OUT: opaque for PTS pointer pointer to formatted content
            out IntPtr pbrkrecpara,             // OUT: pointer to the floater content break record
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            int fserr = PTS.fserrNone;
            try
            {
                FloaterBaseParagraph para = PtsContext.HandleToObject(nmFloater) as FloaterBaseParagraph;
                PTS.ValidateHandle(para);
                FloaterBaseParaClient paraClient = PtsContext.HandleToObject(pfsparaclient) as FloaterBaseParaClient;
                PTS.ValidateHandle(paraClient);
                para.FormatFloaterContentFinite(paraClient, pfsbrkFloaterContentIn, fBreakRecordFromPreviousPage,
                    pftnrej, fEmptyOk, fSuppressTopSpace, fswdirTrack, fAtMaxWidth, durAvailable, dvrAvailable, 
                    fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfsFloatContent, out pbrkrecpara, out durFloaterWidth, 
                    out dvrFloaterHeight, out fsbbox, out cPolygons, out cVertices);
            }
            catch (Exception e)
            {
                fsfmtr = new PTS.FSFMTR(); pfsFloatContent = pbrkrecpara = IntPtr.Zero; durFloaterWidth = dvrFloaterHeight = cPolygons = cVertices = 0; fsbbox = new PTS.FSBBOX();
                PtsContext.CallbackException = e;
                fserr = PTS.fserrCallbackException;
            }
            catch
            {
                fsfmtr = new PTS.FSFMTR(); pfsFloatContent = pbrkrecpara = IntPtr.Zero; durFloaterWidth = dvrFloaterHeight = cPolygons = cVertices = 0; fsbbox = new PTS.FSBBOX();
                PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception");
                fserr = PTS.fserrCallbackException;
            }
            return fserr;
        }
        //-------------------------------------------------------------------
        // UpdateBottomlessFloaterContent
        //-------------------------------------------------------------------
        internal override void UpdateBottomlessFloaterContent(
            FloaterBaseParaClient paraClient,       // IN:
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  direction of track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            IntPtr pfsFloatContent,             // IN:  floater content (in UIElementParagraph, this is an alias to the paraClient)
            out PTS.FSFMTRBL fsfmtrbl,          // OUT: result of formatting
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            fsfmtrbl = default(PTS.FSFMTRBL); 
            durFloaterWidth = dvrFloaterHeight = cPolygons = cVertices = 0; fsbbox = new PTS.FSBBOX();

            Invariant.Assert(false, "No appropriate handling for update in attached object floater.");
        }
        internal override void FormatFloaterContentBottomless(
            FloaterBaseParaClient paraClient,       // IN:
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  direction of track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            out PTS.FSFMTRBL fsfmtrbl,          // OUT: result of formatting
            out IntPtr pfsFloatContent,         // OUT: opaque for PTS pointer pointer to formatted content
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            uint fswdirPara = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty)));

            Invariant.Assert(paraClient is FloaterParaClient);

            int subpageWidth, urSubpageMargin, durSubpageMargin, vrSubpageMargin;
            int dvrTopSpace, fPageBecomesUninterruptable;
            int cColumns;
            PTS.FSCOLUMNINFO[] columnInfoCollection;
            IntPtr pmcsclientOut;
            MbpInfo mbp;
            double specifiedWidth;
            
            // If horizontal alignment is Stretch and we are not formatting at max width,
            // we cannot proceed.
            if (IsFloaterRejected(PTS.ToBoolean(fAtMaxWidth), TextDpi.FromTextDpi(durAvailable)))
            {
                // Set foater width, height to be greater than available values to signal to PTS that floater does not fit in the space
                durFloaterWidth = durAvailable + 1;
                dvrFloaterHeight = dvrAvailable + 1;
                cPolygons = cVertices = 0;
                fsfmtrbl = PTS.FSFMTRBL.fmtrblInterrupted;
                fsbbox = new PTS.FSBBOX();
                fsbbox.fDefined = PTS.False;
                pfsFloatContent = IntPtr.Zero;
            }
            else
            {
                // Initialize the subpage size. PTS subpage margin is always set to 0 for Floaters.
                // If width on floater is specified, use the specified value.
                // Margin, border and padding of the floater is extracted from available subpage width.
                mbp = MbpInfo.FromElement(Element);

                specifiedWidth = CalculateWidth(TextDpi.FromTextDpi(durAvailable));
                AdjustDurAvailable(specifiedWidth, ref durAvailable, out subpageWidth);
                durSubpageMargin = subpageWidth;
                urSubpageMargin = vrSubpageMargin = 0;

                // Initialize column info. Floater always has just 1 column.
                cColumns = 1;
                columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns];
                columnInfoCollection[0].durBefore = 0;
                columnInfoCollection[0].durWidth = subpageWidth;

                // Create subpage
                InvalidateMainTextSegment();
                CreateSubpageBottomlessHelper(PtsContext, _mainTextSegment.Handle, PTS.True,
                    fswdir, subpageWidth, urSubpageMargin, durSubpageMargin, vrSubpageMargin,
                    cColumns, columnInfoCollection,
                    out fsfmtrbl, out pfsFloatContent, out dvrFloaterHeight, out fsbbox, out pmcsclientOut, 
                    out dvrTopSpace, out fPageBecomesUninterruptable);

                if (fsfmtrbl != PTS.FSFMTRBL.fmtrblCollision)
                {
                    // PTS subpage does not support autosizing, but Floater needs to autosize to its
                    // content. To workaround this problem, second format of subpage is performed, if 
                    // necessary. It means that if the width of bounding box is smaller than subpage's
                    // width, second formatting is performed.
                    // However, if HorizontalAlignment is set to Stretch we should not reformat because
                    // floater should be at full column width
                    if (PTS.ToBoolean(fsbbox.fDefined))
                    {
                        if(fsbbox.fsrc.du < subpageWidth && Double.IsNaN(specifiedWidth) && HorizontalAlignment != HorizontalAlignment.Stretch)
                        {
                            // There is a need to reformat PTS subpage, so destroy any resourcces allocated by PTS
                            // during previous formatting.
                            if (pfsFloatContent != IntPtr.Zero)
                            {
                                PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext);
                            }
                            if (pmcsclientOut != IntPtr.Zero)
                            {
                                MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState;
                                PTS.ValidateHandle(mcs);
                                mcs.Dispose();
                                pmcsclientOut = IntPtr.Zero;
                            }
                            // Create subpage with new width.
                            subpageWidth = durSubpageMargin = fsbbox.fsrc.du + 1; // add 1/300px to avoid rounding errors
                            columnInfoCollection[0].durWidth = subpageWidth;
                            CreateSubpageBottomlessHelper(PtsContext, _mainTextSegment.Handle, PTS.True,
                                fswdir, subpageWidth, urSubpageMargin, durSubpageMargin, vrSubpageMargin,
                                cColumns, columnInfoCollection,
                                out fsfmtrbl, out pfsFloatContent, out dvrFloaterHeight, out fsbbox, out pmcsclientOut,
                                out dvrTopSpace, out fPageBecomesUninterruptable);
                        }
                    }
                    else
                    {
                        subpageWidth = TextDpi.ToTextDpi(TextDpi.MinWidth);
                    }

                    // Destroy objects created by PTS, but not used here.
                    if (pmcsclientOut != IntPtr.Zero)
                    {
                        MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState;
                        PTS.ValidateHandle(mcs);
                        mcs.Dispose();
                        pmcsclientOut = IntPtr.Zero;
                    }

                    // Get the size of the floater. For height PTS already reports calculated value.
                    // But width is the same as subpage width. 
                    durFloaterWidth = subpageWidth + mbp.MBPLeft + mbp.MBPRight;

                    dvrFloaterHeight += mbp.MBPTop + mbp.MBPBottom;

                    // Check if floater width fits in available width. It may exceed available width because borders
                    // and padding are added.
                    if ( dvrFloaterHeight > dvrAvailable ||
                         (durFloaterWidth > durAvailable && !PTS.ToBoolean(fAtMaxWidth))
                       )
                    {
                        // Get rid of any previous formatting 
                        if (pfsFloatContent != IntPtr.Zero)
                        {
                            PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext);
                        }

                        Debug.Assert(pmcsclientOut == IntPtr.Zero);
                        cPolygons = cVertices = 0;
                        pfsFloatContent = IntPtr.Zero;
                    }
                    else
                    {
                        // Width and height are OK, format floater
                        // Adjust bounding box to cover entire floater.
                        fsbbox.fsrc.u = 0;
                        fsbbox.fsrc.v = 0;
                        fsbbox.fsrc.du = durFloaterWidth;
                        fsbbox.fsrc.dv = dvrFloaterHeight;

                        // Tight wrap is disabled for now.
                        cPolygons = cVertices = 0;
                    }
                }
                else
                {
                    Debug.Assert(pmcsclientOut == IntPtr.Zero);
                    durFloaterWidth = dvrFloaterHeight = 0;
                    cPolygons = cVertices = 0;
                    pfsFloatContent = IntPtr.Zero;
                }
            }

            // Update handle to PTS subpage.
            ((FloaterParaClient)paraClient).SubpageHandle = pfsFloatContent;
        }
        internal override void FormatFloaterContentFinite(
            FloaterBaseParaClient paraClient,       // IN:
            IntPtr pbrkrecIn,                   // IN:  break record---use if !IntPtr.Zero
            int fBRFromPreviousPage,            // IN:  break record was created on previous page
            IntPtr footnoteRejector,            // IN: 
            int fEmptyOk,                       // IN:  is it OK not to add anything?
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  direction of Track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn,
                                                // IN: suppress breaks at track start?
            out PTS.FSFMTR fsfmtr,              // OUT: result of formatting
            out IntPtr pfsFloatContent,         // OUT: opaque for PTS pointer pointer to formatted content
            out IntPtr pbrkrecOut,              // OUT: pointer to the floater content break record
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            uint fswdirPara = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty)));

            int subpageWidth, subpageHeight;
            int dvrTopSpace;
            int cColumns;
            PTS.FSRECT fsrcSubpageMargin;
            PTS.FSCOLUMNINFO[] columnInfoCollection;
            IntPtr pmcsclientOut;
            double specifiedWidth;
            MbpInfo mbp;

            Invariant.Assert(paraClient is FloaterParaClient);

            // If horizontal alignment is Stretch and we are not formatting at max width,
            // we cannot proceed.
            if (IsFloaterRejected(PTS.ToBoolean(fAtMaxWidth), TextDpi.FromTextDpi(durAvailable)))
            {
                durFloaterWidth = dvrFloaterHeight = 0;
                cPolygons = cVertices = 0;
                fsfmtr = new PTS.FSFMTR();
                fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace;
                fsfmtr.fContainsItemThatStoppedBeforeFootnote = PTS.False;
                fsfmtr.fForcedProgress = PTS.False;
                fsbbox = new PTS.FSBBOX();
                fsbbox.fDefined = PTS.False;
                pbrkrecOut = IntPtr.Zero;
                pfsFloatContent = IntPtr.Zero;
            }
            else
            {
                // When formatting bottomless page, PTS may format paragraphs as finite. This happens
                // in case of multiple columns. In this case make sure that height is not too big.
                if (!StructuralCache.CurrentFormatContext.FinitePage)
                {
                    if (Double.IsInfinity(StructuralCache.CurrentFormatContext.PageHeight))
                    {
                        if (dvrAvailable > PTS.dvBottomUndefined / 2)
                        {
                            dvrAvailable = Math.Min(dvrAvailable, PTS.dvBottomUndefined / 2);
                            fEmptyOk = PTS.False;
                        }
                    }
                    else
                    {
                        dvrAvailable = Math.Min(dvrAvailable, TextDpi.ToTextDpi(StructuralCache.CurrentFormatContext.PageHeight));
                    }
                }

                // Initialize the subpage size. PTS subpage margin is always set to 0 for Floaters.
                // If width on floater is specified, use the specified value.
                // Margin, border and padding of the floater is extracted from available subpage height
                mbp = MbpInfo.FromElement(Element);
                
                // We do not mirror margin as it's used to dist text left and right, and is unnecessary.
                // Clip Floater.Width to available width
                specifiedWidth = CalculateWidth(TextDpi.FromTextDpi(durAvailable));
                AdjustDurAvailable(specifiedWidth, ref durAvailable, out subpageWidth);
                subpageHeight = Math.Max(1, dvrAvailable - (mbp.MBPTop + mbp.MBPBottom));
                fsrcSubpageMargin = new PTS.FSRECT();
                fsrcSubpageMargin.du = subpageWidth;
                fsrcSubpageMargin.dv = subpageHeight;

                // Initialize column info. Floater always has just 1 column.
                cColumns = 1;
                columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns];
                columnInfoCollection[0].durBefore = 0;
                columnInfoCollection[0].durWidth = subpageWidth;

                // Format subpage
                CreateSubpageFiniteHelper(PtsContext, pbrkrecIn, fBRFromPreviousPage, _mainTextSegment.Handle,
                    footnoteRejector, fEmptyOk, PTS.True, fswdir, subpageWidth, subpageHeight,
                    ref fsrcSubpageMargin, cColumns, columnInfoCollection, PTS.False, fsksuppresshardbreakbeforefirstparaIn,
                    out fsfmtr, out pfsFloatContent, 
                    out pbrkrecOut, out dvrFloaterHeight, out fsbbox, out pmcsclientOut, out dvrTopSpace);

                // Initialize subpage metrics
                if (fsfmtr.kstop >= PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace)   // No progress or collision
                {
                    Debug.Assert(pmcsclientOut == IntPtr.Zero);
                    durFloaterWidth = dvrFloaterHeight = 0;
                    cPolygons = cVertices = 0;
                    //pbrkrecpara = IntPtr.Zero;
                }
                else
                {
                    // PTS subpage does not support autosizing, but Floater needs to autosize to its
                    // content. To workaround this problem, second format of subpage is performed, if 
                    // necessary. It means that if the width of bounding box is smaller than subpage's
                    // width, second formatting is performed.
                    // However, if HorizontalAlignment is set to Stretch we should not reformat because floater
                    // should be at max width
                    if (PTS.ToBoolean(fsbbox.fDefined))
                    {
                        if(fsbbox.fsrc.du < subpageWidth && Double.IsNaN(specifiedWidth) && HorizontalAlignment != HorizontalAlignment.Stretch)
                        {
                            // There is a need to reformat PTS subpage, so destroy any resourcces allocated by PTS
                            // during previous formatting.
                            if (pfsFloatContent != IntPtr.Zero)
                            {
                                PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext);
                                pfsFloatContent = IntPtr.Zero;
                            }
                            if (pbrkrecOut != IntPtr.Zero)
                            {
                                PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, pbrkrecOut), PtsContext);
                                pbrkrecOut = IntPtr.Zero;
                            }
                            if (pmcsclientOut != IntPtr.Zero)
                            {
                                MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState;
                                PTS.ValidateHandle(mcs);
                                mcs.Dispose();
                                pmcsclientOut = IntPtr.Zero;
                            }
                            // Create subpage with new width.
                            subpageWidth = fsbbox.fsrc.du + 1; // add 1/300px to avoid rounding errors 
                            fsrcSubpageMargin.du = subpageWidth;
                            fsrcSubpageMargin.dv = subpageHeight;
                            columnInfoCollection[0].durWidth = subpageWidth;
                            CreateSubpageFiniteHelper(PtsContext, pbrkrecIn, fBRFromPreviousPage, _mainTextSegment.Handle,
                                 footnoteRejector, fEmptyOk, PTS.True, fswdir, subpageWidth, subpageHeight,
                                ref fsrcSubpageMargin, cColumns, columnInfoCollection, PTS.False, fsksuppresshardbreakbeforefirstparaIn,
                                out fsfmtr, out pfsFloatContent,
                                out pbrkrecOut, out dvrFloaterHeight, out fsbbox, out pmcsclientOut, out dvrTopSpace);
                        }
                    }
                    else
                    {
                        subpageWidth = TextDpi.ToTextDpi(TextDpi.MinWidth);
                    }

                    // Destroy objects created by PTS, but not used here.
                    if (pmcsclientOut != IntPtr.Zero)
                    {
                        MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState;
                        PTS.ValidateHandle(mcs);
                        mcs.Dispose();
                        pmcsclientOut = IntPtr.Zero;
                    }

                    // Get the size of the floater. For height PTS already reports calculated value.
                    // But width is the same as subpage width. Add margin values here since we do not use 
                    // distance to text anymore
                    durFloaterWidth = subpageWidth + mbp.MBPLeft + mbp.MBPRight;

                    // Add back all MBP values since we do not use dist to text
                    dvrFloaterHeight += mbp.MBPTop + mbp.MBPBottom;
                    // Check if floater width fits in available width. It may exceed available width because borders and
                    // padding are added.


                    fsbbox.fsrc.u = 0;
                    fsbbox.fsrc.v = 0;
                    fsbbox.fsrc.du = durFloaterWidth;
                    fsbbox.fsrc.dv = dvrFloaterHeight;
                    fsbbox.fDefined = PTS.True;

                    // Tight wrap is disabled for now.
                    cPolygons = cVertices = 0;

                    if(durFloaterWidth > durAvailable || dvrFloaterHeight > dvrAvailable)
                    {
                        if(PTS.ToBoolean(fEmptyOk))
                        {
                            // Get rid of any previous formatting 
                            if (pfsFloatContent != IntPtr.Zero)
                            {
                                PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext);
                                pfsFloatContent = IntPtr.Zero;
                            }
                            if (pbrkrecOut != IntPtr.Zero)
                            {
                                PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, pbrkrecOut), PtsContext);
                                pbrkrecOut = IntPtr.Zero;
                            }
                            cPolygons = cVertices = 0;
                            fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace;
                        }
                        else
                        {
                            fsfmtr.fForcedProgress = PTS.True;
                        }
                    }
                }
            }

            // Update handle to PTS subpage.
            ((FloaterParaClient)paraClient).SubpageHandle = pfsFloatContent;
        }
Esempio n. 12
0
        //-------------------------------------------------------------------
        // FormatFloaterContentBottomless
        //-------------------------------------------------------------------
        internal override void FormatFloaterContentBottomless(
            FloaterBaseParaClient paraClient,       // IN:
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  direction of track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            out PTS.FSFMTRBL fsfmtrbl,          // OUT: result of formatting
            out IntPtr pfsFloatContent,         // OUT: opaque for PTS pointer pointer to formatted content
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            Invariant.Assert(paraClient is UIElementParaClient);
            Invariant.Assert(Element is BlockUIContainer);

            if (fAtMaxWidth == PTS.False)
            {
                // BlockUIContainer is only formatted at full column width
                // Set foater width, height to be greater than available values to signal to PTS that floater does not fit in the space
                durFloaterWidth = durAvailable + 1;
                dvrFloaterHeight = dvrAvailable + 1;
                cPolygons = cVertices = 0;
                fsfmtrbl = PTS.FSFMTRBL.fmtrblInterrupted;
                fsbbox = new PTS.FSBBOX();
                fsbbox.fDefined = PTS.False;
                pfsFloatContent = IntPtr.Zero;
            }
            else
            {
                cPolygons = cVertices = 0;

                // Format UIElement
                if (((BlockUIContainer)Element).Child != null)
                {
                    EnsureUIElementIsland();
                    FormatUIElement(durAvailable, out fsbbox);

                    // Set output values for floater content
                    pfsFloatContent = paraClient.Handle;
                    fsfmtrbl = PTS.FSFMTRBL.fmtrblGoalReached;
                    fsbbox.fDefined = PTS.True;
                    durFloaterWidth = fsbbox.fsrc.du;
                    dvrFloaterHeight = fsbbox.fsrc.dv;
                }
                else
                {
                    ClearUIElementIsland();

                    MbpInfo mbp = MbpInfo.FromElement(Element);
                    fsbbox.fsrc = new PTS.FSRECT();
                    fsbbox.fsrc.du = durAvailable;
                    fsbbox.fsrc.dv = mbp.BPTop + mbp.BPBottom;
                    fsbbox.fDefined = PTS.True;
                    pfsFloatContent = paraClient.Handle;
                    fsfmtrbl = PTS.FSFMTRBL.fmtrblGoalReached;
                    durFloaterWidth = fsbbox.fsrc.du;
                    dvrFloaterHeight = fsbbox.fsrc.dv;
                }
            }
        }
Esempio n. 13
0
        //-------------------------------------------------------------------
        // FormatFloaterContentFinite
        //-------------------------------------------------------------------
        internal override void FormatFloaterContentFinite(
            FloaterBaseParaClient paraClient,   // IN:
            IntPtr pbrkrecIn,                   // IN:  break record---use if !IntPtr.Zero
            int fBRFromPreviousPage,            // IN:  break record was created on previous page
            IntPtr footnoteRejector,            // IN: 
            int fEmptyOk,                       // IN:  is it OK not to add anything?
            int fSuppressTopSpace,              // IN:  suppress empty space at the top of the page
            uint fswdir,                        // IN:  direction of Track
            int fAtMaxWidth,                    // IN:  formating is at full width of column
            int durAvailable,                   // IN:  width of available space
            int dvrAvailable,                   // IN:  height of available space
            PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn,
                                                // IN: suppress breaks at track start?
            out PTS.FSFMTR fsfmtr,              // OUT: result of formatting
            out IntPtr pfsFloatContent,         // OUT: opaque for PTS pointer pointer to formatted content
            out IntPtr pbrkrecOut,              // OUT: pointer to the floater content break record
            out int durFloaterWidth,            // OUT: floater width
            out int dvrFloaterHeight,           // OUT: floater height
            out PTS.FSBBOX fsbbox,              // OUT: floater bbox
            out int cPolygons,                  // OUT: number of polygons
            out int cVertices)                  // OUT: total number of vertices in all polygons
        {
            Invariant.Assert(paraClient is UIElementParaClient);
            Invariant.Assert(Element is BlockUIContainer);

            if (fAtMaxWidth == PTS.False && fEmptyOk == PTS.True)
            {
                // Do not format if not at max width, and if fEmptyOk is true
                durFloaterWidth = dvrFloaterHeight = 0;
                cPolygons = cVertices = 0;
                fsfmtr = new PTS.FSFMTR();
                fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace;
                fsfmtr.fContainsItemThatStoppedBeforeFootnote = PTS.False;
                fsfmtr.fForcedProgress = PTS.False;
                fsbbox = new PTS.FSBBOX();
                fsbbox.fDefined = PTS.False;
                pbrkrecOut = IntPtr.Zero;
                pfsFloatContent = IntPtr.Zero;
            }
            else
            {
                cPolygons = cVertices = 0;
                // Formatting is not at max width but proceeds anyway because adding no content is not allowed
                fsfmtr.fForcedProgress = PTS.FromBoolean(fAtMaxWidth == PTS.False);

                // Format UIElement
                if (((BlockUIContainer)Element).Child != null)
                {
                    EnsureUIElementIsland();
                    FormatUIElement(durAvailable, out fsbbox);
                }
                else
                {
                    // Child elementis null. Create fsbbox only with border and padding info
                    ClearUIElementIsland();

                    MbpInfo mbp = MbpInfo.FromElement(Element);
                    fsbbox.fsrc = new PTS.FSRECT();
                    fsbbox.fsrc.du = durAvailable;
                    fsbbox.fsrc.dv = mbp.BPTop + mbp.BPBottom;
                }

                durFloaterWidth = fsbbox.fsrc.du;
                dvrFloaterHeight = fsbbox.fsrc.dv;
                if (dvrAvailable < dvrFloaterHeight && fEmptyOk == PTS.True)
                {
                    // Will not fit in available space. Since fEmptyOk is true, we can return null floater
                    durFloaterWidth = dvrFloaterHeight = 0;
                    fsfmtr = new PTS.FSFMTR();
                    fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace;
                    fsbbox = new PTS.FSBBOX();
                    fsbbox.fDefined = PTS.False;
                    pfsFloatContent = IntPtr.Zero;
                }
                else
                {
                    // Either floater fits in available space or it doesn't but we cannot return nothing. 
                    // Use the space needed, and set formatter result to forced progress if BUC does not fit in available space.
                    fsbbox.fDefined = PTS.True;
                    pfsFloatContent = paraClient.Handle;
                    if (dvrAvailable < dvrFloaterHeight)
                    {
                        // Indicate that progress was forced
                        Invariant.Assert(fEmptyOk == PTS.False);
                        fsfmtr.fForcedProgress = PTS.True;
                    }
                    fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrGoalReached;
                }

                // Set output values for floater content
                pbrkrecOut = IntPtr.Zero;
                fsfmtr.fContainsItemThatStoppedBeforeFootnote = PTS.False;
            }
        }