public abstract SizeF GetSize(Graphics g, DocumentMetrics metrics);
        /// <summary>
        /// Make first calculations to know exactly what we can print on each sheet
        /// </summary>
        /// <param name="g"></param>
        /// <param name="metrics"></param>
        public void Initialize(Graphics g, DocumentMetrics metrics)
        {
            this.metrics = metrics;

            #region Take care of set blocks

            if (SheetHeader != null)
            {
                float headerHeight = SheetHeader.GetSize(g, metrics).Height;

                SheetHeader.Rectangle =
                    new RectangleF(metrics.LeftMargin, metrics.TopMargin, metrics.PrintAbleWidth, headerHeight);

                metrics.TopMargin += (int)Math.Ceiling(headerHeight);
            }

            if (TitlePrintBlock != null)
            {
                float titleHeight = TitlePrintBlock.GetSize(g, metrics).Height;

                TitlePrintBlock.Rectangle =
                    new RectangleF(metrics.LeftMargin, metrics.TopMargin, metrics.PrintAbleWidth, titleHeight);
            }

            if (SheetFooter != null)
            {
                float footerHeight = SheetFooter.GetSize(g, metrics).Height;

                metrics.BottomMargin += (int)Math.Ceiling(footerHeight);
                float footerY = metrics.TopMargin + metrics.PrintAbleHeight;

                SheetFooter.Rectangle =
                    new RectangleF(metrics.LeftMargin, footerY, metrics.PrintAbleWidth, footerHeight);
            }

            #endregion

            columnWidths = new float[GridView.Columns.Count];

            colSelector =
                delegate(DataGridViewColumn column)
                {
                    return (MustPrintSelectedColumns && column.DataGridView.SelectedColumns.Count > 0)
                    ? column.Selected
                    : column.Visible;
                };

            float gridWidth = 0;
            // calculate the grid width to know the difference with a sheet width,
            // it's usefull to determinate a scale
            foreach (DataGridViewColumn column in GridView.Columns)
            {
                if (colSelector(column))
                {
                    columnWidths[column.Index] = GridHelper.Width(column,g);
                    gridWidth += columnWidths[column.Index];
                }
            }

            // we fix the scale to 1 if user doesn't need to zoom out the grid or if
            // the gridwidth is lower thant sheet width
            scale = (!MustFitColumnsToPage || gridWidth < metrics.PrintAbleWidth)
                ? 1 : metrics.PrintAbleWidth / gridWidth;

            columnHeaderHeight = GridHelper.HeaderHeight(GridView,g,scale);

            // let's go to set the partition splits
            List<int> firstColumnsOnPartition = new List<int>();

            float tmpWidth = 0;
            foreach (DataGridViewColumn column in GridView.Columns)
            {
                if (colSelector(column))
                {
                    columnWidths[column.Index] *= scale;

                    tmpWidth += columnWidths[column.Index];

                    if (tmpWidth > metrics.PrintAbleWidth || column.Index == 0)
                    {
                        firstColumnsOnPartition.Insert(0, column.Index);
                        tmpWidth = columnWidths[column.Index];
                    }
                }
            }
            firstColumnsOnPartition.Reverse();

            // let's go to set the level splits

            rowSelector = delegate(DataGridViewRow row)
            {
                return (MustPrintSelectedRows && row.DataGridView.SelectedRows.Count>0)
                    ? row.Selected
                    : row.Visible;
            };

            rowsHeights = new float[GridView.Rows.Count];
            List<int> firstRowsOnPartition = new List<int>();

            // we have to set the first visible row outside of the loop
            // cause we want to take care about the possible set Title block

            IEnumerator rowEnumerator = ((IEnumerable)GridView.Rows).GetEnumerator();
            while(rowEnumerator.MoveNext() && !rowSelector((DataGridViewRow) rowEnumerator.Current)) continue;
            int firstVisibleRowIndex = ((DataGridViewRow)rowEnumerator.Current).Index;

            firstRowsOnPartition.Insert(0, firstVisibleRowIndex);
            rowsHeights[firstVisibleRowIndex] = GridHelper.RowHeight(GridView.Rows[firstVisibleRowIndex],g);

            float tmpHeight = rowsHeights[firstVisibleRowIndex];

            if (TitlePrintBlock != null)
                tmpHeight += TitlePrintBlock.Rectangle.Height;

            // skip the first visible row cause it is already in firstrows (see above)
            while(rowEnumerator.MoveNext())
            {
                DataGridViewRow row = (DataGridViewRow)rowEnumerator.Current;
                if (rowSelector(row))
                {
                    rowsHeights[row.Index] = GridHelper.RowHeight(row, g, scale);
                    tmpHeight += rowsHeights[row.Index];

                    // warn to take care about the column headers
                    if (tmpHeight >= metrics.PrintAbleHeight - columnHeaderHeight)
                    {
                        firstRowsOnPartition.Insert(0, row.Index);
                        tmpHeight = rowsHeights[row.Index];
                    }
                }
            }
            firstRowsOnPartition.Reverse();

            // now that it is calculated, we can fix definitely firstRows and firstColumns in arrays
            firstRows = firstRowsOnPartition.ToArray();
            firstColumns = firstColumnsOnPartition.ToArray();

            levels = new Dictionary<int, IEnumerable<Partition>>(firstRows.Length);

            codes = new Dictionary<CodeEnum, string>();
            codes[CodeEnum.SheetsCount] = SheetsCount.ToString();
            codes[CodeEnum.Date] = DateTime.Now.ToShortDateString();
            codes[CodeEnum.Time] = DateTime.Now.ToShortTimeString();

            IsInitialized = true;
        }