1a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav/* 2a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Copyright (C) 2014 The Android Open Source Project 3a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 4a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Licensed under the Apache License, Version 2.0 (the "License"); 5a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * you may not use this file except in compliance with the License. 6a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * You may obtain a copy of the License at 7a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 8a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * http://www.apache.org/licenses/LICENSE-2.0 9a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 10a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Unless required by applicable law or agreed to in writing, software 11a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * distributed under the License is distributed on an "AS IS" BASIS, 12a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * See the License for the specific language governing permissions and 14a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * limitations under the License. 15a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 16a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 17a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslavpackage com.android.printspooler.util; 18a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 19a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslavimport android.print.PageRange; 20525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintDocumentInfo; 21c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmannimport android.util.Pair; 22a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 23c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmannimport java.util.ArrayList; 24a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslavimport java.util.Arrays; 25a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslavimport java.util.Comparator; 26a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 27a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav/** 28a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * This class contains utility functions for working with page ranges. 29a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 30a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslavpublic final class PageRangeUtils { 31a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 32a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav private static final PageRange[] ALL_PAGES_RANGE = new PageRange[] {PageRange.ALL_PAGES}; 33a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 34a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() { 35a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav @Override 36a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav public int compare(PageRange lhs, PageRange rhs) { 37a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return lhs.getStart() - rhs.getStart(); 38a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 39a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav }; 40a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 41a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav private PageRangeUtils() { 42a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav /* do nothing - hide constructor */ 43a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 44a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 45a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav /** 46525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Gets whether page ranges contains a given page. 47525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * 48525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * @param pageRanges The page ranges. 49525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * @param pageIndex The page for which to check. 50525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * @return Whether the page is within the ranges. 51525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov */ 52525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean contains(PageRange[] pageRanges, int pageIndex) { 53525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov final int rangeCount = pageRanges.length; 54525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov for (int i = 0; i < rangeCount; i++) { 55525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov PageRange pageRange = pageRanges[i]; 56525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (pageRange.contains(pageIndex)) { 57525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return true; 58525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 59525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 60525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return false; 61525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 62525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 63525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov /** 64a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Checks whether one page range array contains another one. 65a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 66a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param ourRanges The container page ranges. 67a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param otherRanges The contained page ranges. 68525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * @param pageCount The total number of pages. 69a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @return Whether the container page ranges contains the contained ones. 70a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 71525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges, int pageCount) { 72a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (ourRanges == null || otherRanges == null) { 73a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return false; 74a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 75a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 76a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (Arrays.equals(ourRanges, ALL_PAGES_RANGE)) { 77a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return true; 78a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 79a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 80525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (Arrays.equals(otherRanges, ALL_PAGES_RANGE)) { 81525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov otherRanges[0] = new PageRange(0, pageCount - 1); 82525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 83525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 84a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav ourRanges = normalize(ourRanges); 85a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav otherRanges = normalize(otherRanges); 86a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 87a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav // Note that the code below relies on the ranges being normalized 88a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav // which is they contain monotonically increasing non-intersecting 89a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav // sub-ranges whose start is less that or equal to the end. 90a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav int otherRangeIdx = 0; 91a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int ourRangeCount = ourRanges.length; 92a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int otherRangeCount = otherRanges.length; 93a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) { 94a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav PageRange ourRange = ourRanges[ourRangeIdx]; 95a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) { 96a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav PageRange otherRange = otherRanges[otherRangeIdx]; 97a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (otherRange.getStart() > ourRange.getEnd()) { 98a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav break; 99a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 100a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (otherRange.getStart() < ourRange.getStart() 101a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav || otherRange.getEnd() > ourRange.getEnd()) { 102a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return false; 103a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 104a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 105a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 106525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return (otherRangeIdx >= otherRangeCount); 107a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 108a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 109a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav /** 110a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Normalizes a page range, which is the resulting page ranges are 111a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * non-overlapping with the start lesser than or equal to the end 112a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * and ordered in an ascending order. 113a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 114a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param pageRanges The page ranges to normalize. 115a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @return The normalized page ranges. 116a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 117a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav public static PageRange[] normalize(PageRange[] pageRanges) { 118a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (pageRanges == null) { 119a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return null; 120a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 121525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 122a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int oldRangeCount = pageRanges.length; 123a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (oldRangeCount <= 1) { 124a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return pageRanges; 125a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 126525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 127a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav Arrays.sort(pageRanges, sComparator); 128525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 129a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav int newRangeCount = 1; 130a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav for (int i = 0; i < oldRangeCount - 1; i++) { 131a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav PageRange currentRange = pageRanges[i]; 132a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav PageRange nextRange = pageRanges[i + 1]; 133a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (currentRange.getEnd() + 1 >= nextRange.getStart()) { 134a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav pageRanges[i] = null; 135a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav pageRanges[i + 1] = new PageRange(currentRange.getStart(), 136a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav Math.max(currentRange.getEnd(), nextRange.getEnd())); 137525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } else { 138525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov newRangeCount++; 139a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 140a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 141525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 142a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (newRangeCount == oldRangeCount) { 143a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return pageRanges; 144a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 145525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 146525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov int normalRangeIndex = 0; 147525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov PageRange[] normalRanges = new PageRange[newRangeCount]; 148525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov for (int i = 0; i < oldRangeCount; i++) { 149525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov PageRange normalRange = pageRanges[i]; 150525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (normalRange != null) { 151525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov normalRanges[normalRangeIndex] = normalRange; 152525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov normalRangeIndex++; 153525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 154525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 155525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 156525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return normalRanges; 157a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 158a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 159a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav /** 160c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * Return the next position after {@code pos} that is not a space character. 161c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 162c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param s The string to parse 163c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param pos The starting position 164c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 165c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @return The position of the first space character 166c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann */ 167c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann private static int readWhiteSpace(CharSequence s, int pos) { 168c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann while (pos < s.length() && s.charAt(pos) == ' ') { 169c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos++; 170c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 171c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 172c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return pos; 173c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 174c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 175c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann /** 176c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * Read a number from a string at a certain position. 177c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 178c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param s The string to parse 179c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param pos The starting position 180c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 181c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @return The position after the number + the number read or null if the number was not found 182c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann */ 183c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann private static Pair<Integer, Integer> readNumber(CharSequence s, int pos) { 184c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Integer result = 0; 185c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann while (pos < s.length() && s.charAt(pos) >= '0' && s.charAt(pos) <= '9') { 186c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // Number cannot start with 0 187c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (result == 0 && s.charAt(pos) == '0') { 188c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann break; 189c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 190c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann result = result * 10 + (s.charAt(pos) - '0'); 191c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // Abort on overflow 192c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (result < 0) { 193c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann break; 194c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 195c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos++; 196c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 197c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 198c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // 0 is not a valid page number 199c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (result == 0) { 200c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos, null); 201c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } else { 202c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos, result); 203c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 204c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 205c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 206c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann /** 207c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * Read a single character from a string at a certain position. 208c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 209c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param s The string to parse 210c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param pos The starting position 211c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param expectedChar The character to read 212c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 213c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @return The position after the character + the character read or null if the character was 214c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * not found 215c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann */ 216c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann private static Pair<Integer, Character> readChar(CharSequence s, int pos, char expectedChar) { 217c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (pos < s.length() && s.charAt(pos) == expectedChar) { 218c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos + 1, expectedChar); 219c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } else { 220c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos, null); 221c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 222c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 223c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 224c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann /** 225c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * Read a page range character from a string at a certain position. 226c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 227c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param s The string to parse 228c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param pos The starting position 229c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param maxPageNumber The highest page number to accept. 230c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 231c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @return The position after the page range + the page range read or null if the page range was 232c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * not found 233c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann */ 234c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann private static Pair<Integer, PageRange> readRange(CharSequence s, int pos, int maxPageNumber) { 235c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Pair<Integer, Integer> retInt; 236c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Pair<Integer, Character> retChar; 237c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 238c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Character comma; 239c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (pos == 0) { 240c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // When we reading the first range, we do not want to have a comma 241c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann comma = ','; 242c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } else { 243c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann retChar = readChar(s, pos, ','); 244c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = retChar.first; 245c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann comma = retChar.second; 246c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 247c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 248c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = readWhiteSpace(s, pos); 249c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 250c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann retInt = readNumber(s, pos); 251c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = retInt.first; 252c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Integer start = retInt.second; 253c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 254c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = readWhiteSpace(s, pos); 255c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 256c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann retChar = readChar(s, pos, '-'); 257c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = retChar.first; 258c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Character separator = retChar.second; 259c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 260c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = readWhiteSpace(s, pos); 261c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 262c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann retInt = readNumber(s, pos); 263c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = retInt.first; 264c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Integer end = retInt.second; 265c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 266c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = readWhiteSpace(s, pos); 267c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 268c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (comma != null && 269c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // range, maybe unbounded 270c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann ((separator != null && (start != null || end != null)) || 271c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann // single page 272c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann (separator == null && start != null && end == null))) { 273c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (start == null) { 274c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann start = 1; 275c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 276c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 277c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (end == null) { 278c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (separator == null) { 279c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann end = start; 280c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } else { 281c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann end = maxPageNumber; 282c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 283c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 284c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 285c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (start <= end && start >= 1 && end <= maxPageNumber) { 286c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos, new PageRange(start - 1, end - 1)); 287c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 288c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 289c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 290c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return new Pair<>(pos, null); 291c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 292c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 293c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann /** 294c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * Parse a string into an array of page ranges. 295c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 296c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param s The string to parse 297c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @param maxPageNumber The highest page number to accept. 298c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * 299c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann * @return The parsed ranges or null if the string could not be parsed. 300c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann */ 301c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann public static PageRange[] parsePageRanges(CharSequence s, int maxPageNumber) { 302c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann ArrayList<PageRange> ranges = new ArrayList<>(); 303c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 304c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann int pos = 0; 305c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann while (pos < s.length()) { 306c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann Pair<Integer, PageRange> retRange = readRange(s, pos, maxPageNumber); 307c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 308c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann if (retRange.second == null) { 309c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann ranges.clear(); 310c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann break; 311c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 312c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 313c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann ranges.add(retRange.second); 314c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann pos = retRange.first; 315c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 316c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 317c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann return PageRangeUtils.normalize(ranges.toArray(new PageRange[ranges.size()])); 318c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann } 319c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann 320c6c319e9889874a1babafa4301dabe193737ebd9Philip P. Moltmann /** 321a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Offsets a the start and end of page ranges with the given value. 322a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 323a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param pageRanges The page ranges to offset. 324a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param offset The offset value. 325a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 326a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav public static void offset(PageRange[] pageRanges, int offset) { 327a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav if (offset == 0) { 328a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav return; 329a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 330a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int pageRangeCount = pageRanges.length; 331a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav for (int i = 0; i < pageRangeCount; i++) { 332a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int start = pageRanges[i].getStart() + offset; 333a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int end = pageRanges[i].getEnd() + offset; 334a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav pageRanges[i] = new PageRange(start, end); 335a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 336a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 337a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav 338a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav /** 339a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * Gets the number of pages in a normalized range array. 340a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * 341a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param pageRanges Normalized page ranges. 342a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @param layoutPageCount Page count after reported after layout pass. 343a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav * @return The page count in the ranges. 344a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav */ 345a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav public static int getNormalizedPageCount(PageRange[] pageRanges, int layoutPageCount) { 346a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav int pageCount = 0; 347525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (pageRanges != null) { 348525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov final int pageRangeCount = pageRanges.length; 349525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov for (int i = 0; i < pageRangeCount; i++) { 350525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov PageRange pageRange = pageRanges[i]; 351525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (PageRange.ALL_PAGES.equals(pageRange)) { 352525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return layoutPageCount; 353525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 354525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov pageCount += pageRange.getSize(); 355525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 356525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 357525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return pageCount; 358525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 359525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 360525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static PageRange asAbsoluteRange(PageRange pageRange, int pageCount) { 361525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (PageRange.ALL_PAGES.equals(pageRange)) { 362525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return new PageRange(0, pageCount - 1); 363525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 364525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return pageRange; 365525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 366525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 367525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean isAllPages(PageRange[] pageRanges) { 368a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav final int pageRangeCount = pageRanges.length; 369a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav for (int i = 0; i < pageRangeCount; i++) { 370a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav PageRange pageRange = pageRanges[i]; 371525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (isAllPages(pageRange)) { 372525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return true; 373a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 374a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 375525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return false; 376525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 377525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 378525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean isAllPages(PageRange pageRange) { 379525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return PageRange.ALL_PAGES.equals(pageRange); 380525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 381525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 382525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean isAllPages(PageRange[] pageRanges, int pageCount) { 383525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov final int pageRangeCount = pageRanges.length; 384525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov for (int i = 0; i < pageRangeCount; i++) { 385525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov PageRange pageRange = pageRanges[i]; 386525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov if (isAllPages(pageRange, pageCount)) { 387525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return true; 388525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 389525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 390525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return false; 391525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 392525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 393525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov public static boolean isAllPages(PageRange pageRanges, int pageCount) { 394525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return pageRanges.getStart() == 0 && pageRanges.getEnd() == pageCount - 1; 395525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 396525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 397f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann /** 398f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * Compute the pages of the file that correspond to the requested pages in the doc. 399f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * 400f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * @param pagesInDocRequested The requested pages, doc-indexed 401f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * @param pagesWrittenToFile The pages in the file 402f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * @param pageCount The number of pages in the doc 403f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * 404f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann * @return The pages, file-indexed 405f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann */ 406f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann public static PageRange[] computeWhichPagesInFileToPrint(PageRange[] pagesInDocRequested, 407f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann PageRange[] pagesWrittenToFile, int pageCount) { 408525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // Adjust the print job pages based on what was requested and written. 409525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // The cases are ordered in the most expected to the least expected 410525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // with a special case first where the app does not know the page count 411525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // so we ask for all to be written. 412f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann if (Arrays.equals(pagesInDocRequested, ALL_PAGES_RANGE) 413525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov && pageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) { 414525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return ALL_PAGES_RANGE; 415f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann } else if (Arrays.equals(pagesWrittenToFile, pagesInDocRequested)) { 416525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // We got a document with exactly the pages we wanted. Hence, 417525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // the printer has to print all pages in the data. 418525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return ALL_PAGES_RANGE; 419f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann } else if (Arrays.equals(pagesWrittenToFile, ALL_PAGES_RANGE)) { 420525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // We requested specific pages but got all of them. Hence, 421525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // the printer has to print only the requested pages. 422f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann return pagesInDocRequested; 423f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann } else if (PageRangeUtils.contains(pagesWrittenToFile, pagesInDocRequested, pageCount)) { 424525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // We requested specific pages and got more but not all pages. 425525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // Hence, we have to offset appropriately the printed pages to 426525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // be based off the start of the written ones instead of zero. 427525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // The written pages are always non-null and not empty. 428f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann final int offset = -pagesWrittenToFile[0].getStart(); 429bdace63d5bda8a3f19300b52064a0f77bc5d3754Philip P. Moltmann PageRangeUtils.offset(pagesInDocRequested.clone(), offset); 430f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann return pagesInDocRequested; 431f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann } else if (Arrays.equals(pagesInDocRequested, ALL_PAGES_RANGE) 432f7a5b4fb30792789d9e436bb5107f1a6d743d49cPhilip P. Moltmann && isAllPages(pagesWrittenToFile, pageCount)) { 433525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // We requested all pages via the special constant and got all 434525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // of them as an explicit enumeration. Hence, the printer has 435525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov // to print only the requested pages. 436525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return ALL_PAGES_RANGE; 437525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov } 438525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov 439525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov return null; 440a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav } 441a798c0a984f29f7180883a61839f68d2cbf0c6ceSvetoslav} 442