/* * Copyright 2007, Plutext Pty Ltd. * * This file is part of Docx4all. Docx4all is free software: you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. Docx4all is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Docx4all. If not, see <http://www.gnu.org/licenses/>. */ package org.docx4all.swing.text; import java.io.Serializable; import javax.swing.SizeRequirements; /** * This file is based on javax.swing.text.html.CSS class downloaded from * <a href="http://download.java.net/openjdk/jdk7/">OpenJDK Source Releases</a> */ public class CSS implements Serializable { /** * Calculate the requirements needed to tile the requirements * given by the iterator that would be tiled. The calculation * takes into consideration margin and border spacing. */ static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) { long minimum = 0; long maximum = 0; long preferred = 0; int lastMargin = 0; int totalSpacing = 0; int n = iter.getCount(); for (int i = 0; i < n; i++) { iter.setIndex(i); int margin0 = lastMargin; int margin1 = (int) iter.getLeadingCollapseSpan(); totalSpacing += Math.max(margin0, margin1); preferred += (int) iter.getPreferredSpan(0); minimum += iter.getMinimumSpan(0); maximum += iter.getMaximumSpan(0); lastMargin = (int) iter.getTrailingCollapseSpan(); } totalSpacing += lastMargin; totalSpacing += 2 * iter.getBorderWidth(); // adjust for the spacing area minimum += totalSpacing; preferred += totalSpacing; maximum += totalSpacing; // set return value if (r == null) { r = new SizeRequirements(); } r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum; r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred; r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum; return r; } /** * Calculate a tiled layout for the given iterator. * This should be done collapsing the neighboring * margins to be a total of the maximum of the two * neighboring margin areas as described in the CSS spec. */ static void calculateTiledLayout(LayoutIterator iter, int targetSpan) { /* * first pass, calculate the preferred sizes, adjustments needed because * of margin collapsing, and the flexibility to adjust the sizes. */ long preferred = 0; long currentPreferred; int lastMargin = 0; int totalSpacing = 0; int n = iter.getCount(); int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1; //max gain we can get adjusting elements with adjustmentWeight <= i long gain[] = new long[adjustmentWeightsCount]; //max loss we can get adjusting elements with adjustmentWeight <= i long loss[] = new long[adjustmentWeightsCount]; for (int i = 0; i < adjustmentWeightsCount; i++) { gain[i] = loss[i] = 0; } for (int i = 0; i < n; i++) { iter.setIndex(i); int margin0 = lastMargin; int margin1 = (int) iter.getLeadingCollapseSpan(); iter.setOffset(Math.max(margin0, margin1)); totalSpacing += iter.getOffset(); currentPreferred = (long)iter.getPreferredSpan(targetSpan); iter.setSpan((int) currentPreferred); preferred += currentPreferred; gain[iter.getAdjustmentWeight()] += (long)iter.getMaximumSpan(targetSpan) - currentPreferred; loss[iter.getAdjustmentWeight()] += currentPreferred - (long)iter.getMinimumSpan(targetSpan); lastMargin = (int) iter.getTrailingCollapseSpan(); } totalSpacing += lastMargin; totalSpacing += 2 * iter.getBorderWidth(); for (int i = 1; i < adjustmentWeightsCount; i++) { gain[i] += gain[i - 1]; loss[i] += loss[i - 1]; } /* * Second pass, expand or contract by as much as possible to reach * the target span. This takes the margin collapsing into account * prior to adjusting the span. */ // determine the adjustment to be made int allocated = targetSpan - totalSpacing; long desiredAdjustment = allocated - preferred; long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss; desiredAdjustment = Math.abs(desiredAdjustment); int adjustmentLevel = 0; for (;adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight; adjustmentLevel++) { // adjustmentsArray[] is sorted. I do not bother about // binary search though if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) { break; } } float adjustmentFactor = 0.0f; if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) { desiredAdjustment -= (adjustmentLevel > 0) ? adjustmentsArray[adjustmentLevel - 1] : 0; if (desiredAdjustment != 0) { float maximumAdjustment = adjustmentsArray[adjustmentLevel] - ((adjustmentLevel > 0) ? adjustmentsArray[adjustmentLevel - 1] : 0 ); adjustmentFactor = desiredAdjustment / maximumAdjustment; } } // make the adjustments int totalOffset = (int)iter.getBorderWidth(); for (int i = 0; i < n; i++) { iter.setIndex(i); iter.setOffset( iter.getOffset() + totalOffset); if (iter.getAdjustmentWeight() < adjustmentLevel) { iter.setSpan((int) ((allocated > preferred) ? Math.floor(iter.getMaximumSpan(targetSpan)) : Math.ceil(iter.getMinimumSpan(targetSpan)) ) ); } else if (iter.getAdjustmentWeight() == adjustmentLevel) { int availableSpan = (allocated > preferred) ? (int) iter.getMaximumSpan(targetSpan) - iter.getSpan() : iter.getSpan() - (int) iter.getMinimumSpan(targetSpan); int adj = (int)Math.floor(adjustmentFactor * availableSpan); iter.setSpan(iter.getSpan() + ((allocated > preferred) ? adj : -adj)); } totalOffset = (int) Math.min((long) iter.getOffset() + (long) iter.getSpan(), Integer.MAX_VALUE); } // while rounding we could lose several pixels. int roundError = targetSpan - totalOffset - (int)iter.getTrailingCollapseSpan() - (int)iter.getBorderWidth(); int adj = (roundError > 0) ? 1 : -1; roundError *= adj; boolean canAdjust = true; while (roundError > 0 && canAdjust) { // check for infinite loop canAdjust = false; int offsetAdjust = 0; // try to distribute roundError. one pixel per cell for (int i = 0; i < n; i++) { iter.setIndex(i); iter.setOffset(iter.getOffset() + offsetAdjust); int curSpan = iter.getSpan(); if (roundError > 0) { int boundGap = (adj > 0) ? (int)Math.floor(iter.getMaximumSpan(targetSpan)) - curSpan : curSpan - (int)Math.ceil(iter.getMinimumSpan(targetSpan)); if (boundGap >= 1) { canAdjust = true; iter.setSpan(curSpan + adj); offsetAdjust += adj; roundError--; } } } } } /** * An iterator to express the requirements to use when computing * layout. */ interface LayoutIterator { void setOffset(int offs); int getOffset(); void setSpan(int span); int getSpan(); int getCount(); void setIndex(int i); float getMinimumSpan(float parentSpan); float getPreferredSpan(float parentSpan); float getMaximumSpan(float parentSpan); int getAdjustmentWeight(); //0 is the best weight WorstAdjustmentWeight is a worst one //float getAlignment(); float getBorderWidth(); float getLeadingCollapseSpan(); float getTrailingCollapseSpan(); public static final int WorstAdjustmentWeight = 2; } }