1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.printspooler.util; 18 19 import android.print.PageRange; 20 import android.print.PrintDocumentInfo; 21 22 import java.util.Arrays; 23 import java.util.Comparator; 24 25 /** 26 * This class contains utility functions for working with page ranges. 27 */ 28 public final class PageRangeUtils { 29 30 private static final PageRange[] ALL_PAGES_RANGE = new PageRange[] {PageRange.ALL_PAGES}; 31 32 private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() { 33 @Override 34 public int compare(PageRange lhs, PageRange rhs) { 35 return lhs.getStart() - rhs.getStart(); 36 } 37 }; 38 39 private PageRangeUtils() { 40 /* do nothing - hide constructor */ 41 } 42 43 /** 44 * Gets whether page ranges contains a given page. 45 * 46 * @param pageRanges The page ranges. 47 * @param pageIndex The page for which to check. 48 * @return Whether the page is within the ranges. 49 */ 50 public static boolean contains(PageRange[] pageRanges, int pageIndex) { 51 final int rangeCount = pageRanges.length; 52 for (int i = 0; i < rangeCount; i++) { 53 PageRange pageRange = pageRanges[i]; 54 if (pageRange.contains(pageIndex)) { 55 return true; 56 } 57 } 58 return false; 59 } 60 61 /** 62 * Checks whether one page range array contains another one. 63 * 64 * @param ourRanges The container page ranges. 65 * @param otherRanges The contained page ranges. 66 * @param pageCount The total number of pages. 67 * @return Whether the container page ranges contains the contained ones. 68 */ 69 public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges, int pageCount) { 70 if (ourRanges == null || otherRanges == null) { 71 return false; 72 } 73 74 if (Arrays.equals(ourRanges, ALL_PAGES_RANGE)) { 75 return true; 76 } 77 78 if (Arrays.equals(otherRanges, ALL_PAGES_RANGE)) { 79 otherRanges[0] = new PageRange(0, pageCount - 1); 80 } 81 82 ourRanges = normalize(ourRanges); 83 otherRanges = normalize(otherRanges); 84 85 // Note that the code below relies on the ranges being normalized 86 // which is they contain monotonically increasing non-intersecting 87 // sub-ranges whose start is less that or equal to the end. 88 int otherRangeIdx = 0; 89 final int ourRangeCount = ourRanges.length; 90 final int otherRangeCount = otherRanges.length; 91 for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) { 92 PageRange ourRange = ourRanges[ourRangeIdx]; 93 for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) { 94 PageRange otherRange = otherRanges[otherRangeIdx]; 95 if (otherRange.getStart() > ourRange.getEnd()) { 96 break; 97 } 98 if (otherRange.getStart() < ourRange.getStart() 99 || otherRange.getEnd() > ourRange.getEnd()) { 100 return false; 101 } 102 } 103 } 104 return (otherRangeIdx >= otherRangeCount); 105 } 106 107 /** 108 * Normalizes a page range, which is the resulting page ranges are 109 * non-overlapping with the start lesser than or equal to the end 110 * and ordered in an ascending order. 111 * 112 * @param pageRanges The page ranges to normalize. 113 * @return The normalized page ranges. 114 */ 115 public static PageRange[] normalize(PageRange[] pageRanges) { 116 if (pageRanges == null) { 117 return null; 118 } 119 120 final int oldRangeCount = pageRanges.length; 121 if (oldRangeCount <= 1) { 122 return pageRanges; 123 } 124 125 Arrays.sort(pageRanges, sComparator); 126 127 int newRangeCount = 1; 128 for (int i = 0; i < oldRangeCount - 1; i++) { 129 PageRange currentRange = pageRanges[i]; 130 PageRange nextRange = pageRanges[i + 1]; 131 if (currentRange.getEnd() + 1 >= nextRange.getStart()) { 132 pageRanges[i] = null; 133 pageRanges[i + 1] = new PageRange(currentRange.getStart(), 134 Math.max(currentRange.getEnd(), nextRange.getEnd())); 135 } else { 136 newRangeCount++; 137 } 138 } 139 140 if (newRangeCount == oldRangeCount) { 141 return pageRanges; 142 } 143 144 int normalRangeIndex = 0; 145 PageRange[] normalRanges = new PageRange[newRangeCount]; 146 for (int i = 0; i < oldRangeCount; i++) { 147 PageRange normalRange = pageRanges[i]; 148 if (normalRange != null) { 149 normalRanges[normalRangeIndex] = normalRange; 150 normalRangeIndex++; 151 } 152 } 153 154 return normalRanges; 155 } 156 157 /** 158 * Offsets a the start and end of page ranges with the given value. 159 * 160 * @param pageRanges The page ranges to offset. 161 * @param offset The offset value. 162 */ 163 public static void offset(PageRange[] pageRanges, int offset) { 164 if (offset == 0) { 165 return; 166 } 167 final int pageRangeCount = pageRanges.length; 168 for (int i = 0; i < pageRangeCount; i++) { 169 final int start = pageRanges[i].getStart() + offset; 170 final int end = pageRanges[i].getEnd() + offset; 171 pageRanges[i] = new PageRange(start, end); 172 } 173 } 174 175 /** 176 * Gets the number of pages in a normalized range array. 177 * 178 * @param pageRanges Normalized page ranges. 179 * @param layoutPageCount Page count after reported after layout pass. 180 * @return The page count in the ranges. 181 */ 182 public static int getNormalizedPageCount(PageRange[] pageRanges, int layoutPageCount) { 183 int pageCount = 0; 184 if (pageRanges != null) { 185 final int pageRangeCount = pageRanges.length; 186 for (int i = 0; i < pageRangeCount; i++) { 187 PageRange pageRange = pageRanges[i]; 188 if (PageRange.ALL_PAGES.equals(pageRange)) { 189 return layoutPageCount; 190 } 191 pageCount += pageRange.getSize(); 192 } 193 } 194 return pageCount; 195 } 196 197 public static PageRange asAbsoluteRange(PageRange pageRange, int pageCount) { 198 if (PageRange.ALL_PAGES.equals(pageRange)) { 199 return new PageRange(0, pageCount - 1); 200 } 201 return pageRange; 202 } 203 204 public static boolean isAllPages(PageRange[] pageRanges) { 205 final int pageRangeCount = pageRanges.length; 206 for (int i = 0; i < pageRangeCount; i++) { 207 PageRange pageRange = pageRanges[i]; 208 if (isAllPages(pageRange)) { 209 return true; 210 } 211 } 212 return false; 213 } 214 215 public static boolean isAllPages(PageRange pageRange) { 216 return PageRange.ALL_PAGES.equals(pageRange); 217 } 218 219 public static boolean isAllPages(PageRange[] pageRanges, int pageCount) { 220 final int pageRangeCount = pageRanges.length; 221 for (int i = 0; i < pageRangeCount; i++) { 222 PageRange pageRange = pageRanges[i]; 223 if (isAllPages(pageRange, pageCount)) { 224 return true; 225 } 226 } 227 return false; 228 } 229 230 public static boolean isAllPages(PageRange pageRanges, int pageCount) { 231 return pageRanges.getStart() == 0 && pageRanges.getEnd() == pageCount - 1; 232 } 233 234 public static PageRange[] computePrintedPages(PageRange[] requestedPages, 235 PageRange[] writtenPages, int pageCount) { 236 // Adjust the print job pages based on what was requested and written. 237 // The cases are ordered in the most expected to the least expected 238 // with a special case first where the app does not know the page count 239 // so we ask for all to be written. 240 if (Arrays.equals(requestedPages, ALL_PAGES_RANGE) 241 && pageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) { 242 return ALL_PAGES_RANGE; 243 } else if (Arrays.equals(writtenPages, requestedPages)) { 244 // We got a document with exactly the pages we wanted. Hence, 245 // the printer has to print all pages in the data. 246 return ALL_PAGES_RANGE; 247 } else if (Arrays.equals(writtenPages, ALL_PAGES_RANGE)) { 248 // We requested specific pages but got all of them. Hence, 249 // the printer has to print only the requested pages. 250 return requestedPages; 251 } else if (PageRangeUtils.contains(writtenPages, requestedPages, pageCount)) { 252 // We requested specific pages and got more but not all pages. 253 // Hence, we have to offset appropriately the printed pages to 254 // be based off the start of the written ones instead of zero. 255 // The written pages are always non-null and not empty. 256 final int offset = -writtenPages[0].getStart(); 257 PageRangeUtils.offset(requestedPages, offset); 258 return requestedPages; 259 } else if (Arrays.equals(requestedPages, ALL_PAGES_RANGE) 260 && isAllPages(writtenPages, pageCount)) { 261 // We requested all pages via the special constant and got all 262 // of them as an explicit enumeration. Hence, the printer has 263 // to print only the requested pages. 264 return ALL_PAGES_RANGE; 265 } 266 267 return null; 268 } 269 } 270