aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-lib
diff options
context:
space:
mode:
authorAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2013-07-30 15:45:49 +0200
committerAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2013-07-30 15:45:49 +0200
commitc33195994e0a5e263ebb87402f9789cdda21a4b2 (patch)
tree322cccf9924977f4feb8f345761f8ece455fb9e7 /pdf-as-lib
parent203620ec8d0121f0786518a1a9bec8ba4a5e8fe1 (diff)
downloadpdf-as-3-c33195994e0a5e263ebb87402f9789cdda21a4b2.tar.gz
pdf-as-3-c33195994e0a5e263ebb87402f9789cdda21a4b2.tar.bz2
pdf-as-3-c33195994e0a5e263ebb87402f9789cdda21a4b2.zip
Fixed legacy parameter reading with trim
Diffstat (limited to 'pdf-as-lib')
-rw-r--r--pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java641
1 files changed, 319 insertions, 322 deletions
diff --git a/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java
index a851e18..138f334 100644
--- a/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java
+++ b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java
@@ -73,80 +73,83 @@ import at.knowcenter.wag.egov.egiz.pdf.operator.path.painting.StrokePath;
* This method is called when processing the FileStream. By calling the method
* {@link org.pdfbox.util.PDFStreamEngine#processStream(org.pdfbox.pdmodel.PDPage, org.pdfbox.pdmodel.PDResources, org.pdfbox.cos.COSStream)}
* the implemented method showCharacter is called.
- *
+ *
* @author wlackner
* @see PDFTextStripper
*/
-public class PDFPage extends PDFTextStripper
-{
- /**
- * The logger definition.
- */
- private static final Logger logger_ = ConfigLogger.getLogger(PDFPage.class);
-
- /**
- * The maximum (lowest) y position of a character.
- */
- protected float max_character_ypos = Float.NEGATIVE_INFINITY;
-
- /**
- * The maximum (lowest y position of an image.
- */
- protected float max_image_ypos = Float.NEGATIVE_INFINITY;
-
- /**
- * The effective page height.
- */
- protected float effectivePageHeight;
-
- /**
- * The path currently being constructed.
- */
- private GeneralPath currentPath = new GeneralPath();
-
- /**
- * The lowest position of a drawn path (originating from top).
- */
- private float maxPathRelatedYPositionFromTop = Float.NEGATIVE_INFINITY;
-
- /**
- * Constructor.
- *
- * @param effectivePageHeight The height of the page to be evaluated. PDF elements outside this height will not be considered.
- *
- * @throws IOException
- */
- public PDFPage(float effectivePageHeight) throws IOException
- {
- super();
-
- this.effectivePageHeight = effectivePageHeight;
-
- OperatorProcessor newInvoke = new MyInvoke();
- newInvoke.setContext(this);
- operators.put("Do", newInvoke);
-
- boolean legacy = false;
-
- try {
- String leg = SettingsReader.getInstance().getSetting("legacy_positioning", "false");
- if("true".equals(leg)) {
- legacy = true;
- }
- } catch (SettingsException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- if(!legacy) {
- registerCustomPathOperators();
- }
- }
+public class PDFPage extends PDFTextStripper {
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger_ = ConfigLogger.getLogger(PDFPage.class);
/**
- * Registers operators responsible for path construction and painting in order to fix auto positioning on pages with
- * path elements.
- *
+ * The maximum (lowest) y position of a character.
+ */
+ protected float max_character_ypos = Float.NEGATIVE_INFINITY;
+
+ /**
+ * The maximum (lowest y position of an image.
+ */
+ protected float max_image_ypos = Float.NEGATIVE_INFINITY;
+
+ /**
+ * The effective page height.
+ */
+ protected float effectivePageHeight;
+
+ /**
+ * The path currently being constructed.
+ */
+ private GeneralPath currentPath = new GeneralPath();
+
+ /**
+ * The lowest position of a drawn path (originating from top).
+ */
+ private float maxPathRelatedYPositionFromTop = Float.NEGATIVE_INFINITY;
+
+ /**
+ * Constructor.
+ *
+ * @param effectivePageHeight
+ * The height of the page to be evaluated. PDF elements outside
+ * this height will not be considered.
+ *
+ * @throws IOException
+ */
+ public PDFPage(float effectivePageHeight) throws IOException {
+ super();
+
+ this.effectivePageHeight = effectivePageHeight;
+
+ OperatorProcessor newInvoke = new MyInvoke();
+ newInvoke.setContext(this);
+ operators.put("Do", newInvoke);
+
+ boolean legacy = false;
+
+ try {
+ String leg = SettingsReader.getInstance().getSetting("legacy.pos",
+ "false");
+ if (leg != null) {
+ if ("true".equals(leg.trim())) {
+ legacy = true;
+ }
+ }
+ } catch (SettingsException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (!legacy) {
+ registerCustomPathOperators();
+ }
+ }
+
+ /**
+ * Registers operators responsible for path construction and painting in
+ * order to fix auto positioning on pages with path elements.
+ *
* @author Datentechnik Innovation GmbH
*/
@SuppressWarnings("unchecked")
@@ -175,16 +178,19 @@ public class PDFPage extends PDFTextStripper
operators.put("B*", new FillEvenOddAndStrokePath(this));
operators.put("n", new EndPath(this));
- // Note: The graphic context (org.pdfbox.pdmodel.graphics.PDGraphicsState) of the underlying pdfbox library does
- // not yet support clipping. This prevents feasible usage of clipping operators (W, W*).
-// operators.put("W", new ...(this));
-// operators.put("W*", new ...(this));
+ // Note: The graphic context
+ // (org.pdfbox.pdmodel.graphics.PDGraphicsState) of the underlying
+ // pdfbox library does
+ // not yet support clipping. This prevents feasible usage of clipping
+ // operators (W, W*).
+ // operators.put("W", new ...(this));
+ // operators.put("W*", new ...(this));
}
/**
* Returns the path currently being constructed.
- *
+ *
* @return The path currently being constructed.
*/
public GeneralPath getCurrentPath() {
@@ -193,7 +199,9 @@ public class PDFPage extends PDFTextStripper
/**
* Sets the current path.
- * @param currentPath The new current path.
+ *
+ * @param currentPath
+ * The new current path.
*/
public void setCurrentPath(GeneralPath currentPath) {
this.currentPath = currentPath;
@@ -201,9 +209,10 @@ public class PDFPage extends PDFTextStripper
/**
* Registers a rectangle that bounds the path currently being drawn.
- *
+ *
* @param bounds
- * A rectangle depicting the bounds (coordinates originating from bottom left).
+ * A rectangle depicting the bounds (coordinates originating from
+ * bottom left).
* @author Datentechnik Innovation GmbH
*/
public void registerPathBounds(Rectangle bounds) {
@@ -233,257 +242,250 @@ public class PDFPage extends PDFTextStripper
break;
case 270: // CCW
pageHeight = boundaryBox.getWidth();
- upperBoundYPositionFromTop = pageHeight - (float) bounds.getMaxX();
- lowerBoundYPositionFromTop = pageHeight - (float) bounds.getMinX();
+ upperBoundYPositionFromTop = pageHeight
+ - (float) bounds.getMaxX();
+ lowerBoundYPositionFromTop = pageHeight
+ - (float) bounds.getMinX();
break;
default:
pageHeight = boundaryBox.getHeight();
- upperBoundYPositionFromTop = pageHeight - (float) bounds.getMaxY();
- lowerBoundYPositionFromTop = pageHeight - (float) bounds.getMinY();
+ upperBoundYPositionFromTop = pageHeight
+ - (float) bounds.getMaxY();
+ lowerBoundYPositionFromTop = pageHeight
+ - (float) bounds.getMinY();
break;
}
// new maximum ?
if (lowerBoundYPositionFromTop > maxPathRelatedYPositionFromTop) {
- // Is the rectangle (at least partly) located above the footer line?
+ // Is the rectangle (at least partly) located above the footer
+ // line?
// (effective page height := page height - footer line)
if (upperBoundYPositionFromTop <= effectivePageHeight) {
// yes: update current end of path-related page content
maxPathRelatedYPositionFromTop = lowerBoundYPositionFromTop;
- logger_.trace("New max path related y position (from top): " + maxPathRelatedYPositionFromTop);
+ logger_.trace("New max path related y position (from top): "
+ + maxPathRelatedYPositionFromTop);
} else {
- // no: rectangle is fully located below the footer line -> ignore
+ // no: rectangle is fully located below the footer line ->
+ // ignore
logger_.trace("Ignoring path bound below the footer line.");
}
}
}
}
- protected void processOperator(PDFOperator operator, List arguments) throws IOException
- {
-// logger_.debug("operator = " + operator);
- super.processOperator(operator, arguments);
- }
-
- // exthex
- /**
- * A method provided as an event interface to allow a subclass to perform some
- * specific functionality when a character needs to be displayed. This method
- * is used to calculate the latest position of a text in the page. Sorry for
- * this missinterpretation of the method, but it is the only way to do this
- * (provided by PDFBox)!!!
- *
- * @param text
- * the character to be displayed -> calculate there y position.
- */
- protected void showCharacter(TextPosition text)
- {
- float current_y = text.getY();
- final String character = text.getCharacter();
-
- int pageRotation = page.findRotation();
- //logger_.debug("PageRotation = " + pageRotation);
- if (pageRotation == 0)
- {
- current_y = text.getY();
- }
- if (pageRotation == 90)
- {
- current_y = text.getX();
- }
- if (pageRotation == 180)
- {
- float page_height = page.findMediaBox().getHeight();
- current_y = page_height - text.getY();
- }
- if (pageRotation == 270)
- {
- float page_height = page.findMediaBox().getHeight();
- current_y = page_height - text.getX();
- }
-
- if (current_y > this.effectivePageHeight)
- {
- //logger_.debug("character is below footer_line. footer_line = " + this.footer_line + ", text.character=" + character + ", y=" + current_y);
- return;
- }
-
- // store ypos of the char if it is not empty
- if (!character.equals(" ") && current_y > this.max_character_ypos)
- {
- this.max_character_ypos = current_y;
- }
-
- }
-
- // use this funtion getting an unsorted text output
- // public void showString(byte[] string) {
- // logger_.debug(new String(string));
- // }
+ protected void processOperator(PDFOperator operator, List arguments)
+ throws IOException {
+ // logger_.debug("operator = " + operator);
+ super.processOperator(operator, arguments);
+ }
+
+ // exthex
+ /**
+ * A method provided as an event interface to allow a subclass to perform
+ * some specific functionality when a character needs to be displayed. This
+ * method is used to calculate the latest position of a text in the page.
+ * Sorry for this missinterpretation of the method, but it is the only way
+ * to do this (provided by PDFBox)!!!
+ *
+ * @param text
+ * the character to be displayed -> calculate there y position.
+ */
+ protected void showCharacter(TextPosition text) {
+ float current_y = text.getY();
+ final String character = text.getCharacter();
+
+ int pageRotation = page.findRotation();
+ // logger_.debug("PageRotation = " + pageRotation);
+ if (pageRotation == 0) {
+ current_y = text.getY();
+ }
+ if (pageRotation == 90) {
+ current_y = text.getX();
+ }
+ if (pageRotation == 180) {
+ float page_height = page.findMediaBox().getHeight();
+ current_y = page_height - text.getY();
+ }
+ if (pageRotation == 270) {
+ float page_height = page.findMediaBox().getHeight();
+ current_y = page_height - text.getX();
+ }
+
+ if (current_y > this.effectivePageHeight) {
+ // logger_.debug("character is below footer_line. footer_line = " +
+ // this.footer_line + ", text.character=" + character + ", y=" +
+ // current_y);
+ return;
+ }
+
+ // store ypos of the char if it is not empty
+ if (!character.equals(" ") && current_y > this.max_character_ypos) {
+ this.max_character_ypos = current_y;
+ }
+
+ }
+
+ // use this funtion getting an unsorted text output
+ // public void showString(byte[] string) {
+ // logger_.debug(new String(string));
+ // }
/**
* Returns the calculated page length.
- *
+ *
* @return the max page length value
*/
public float getMaxPageLength() {
if (logger_.isDebugEnabled()) {
- logger_.debug("Determining page content length: text=" + max_character_ypos + ", image=" + max_image_ypos
+ logger_.debug("Determining page content length: text="
+ + max_character_ypos + ", image=" + max_image_ypos
+ ", path=" + maxPathRelatedYPositionFromTop);
}
- return NumberUtils.max(max_character_ypos, max_image_ypos, maxPathRelatedYPositionFromTop);
+ return NumberUtils.max(max_character_ypos, max_image_ypos,
+ maxPathRelatedYPositionFromTop);
+ }
+
+ public class MyInvoke extends OperatorProcessor {
+
+ public void process(PDFOperator operator, List arguments)
+ throws IOException {
+ COSName name = (COSName) arguments.get(0);
+
+ // PDResources res = context.getResources();
+
+ Map xobjects = context.getXObjects();
+ PDXObject xobject = (PDXObject) xobjects.get(name.getName());
+
+ PDStream stream = xobject.getPDStream();
+ COSStream cos_stream = stream.getStream();
+
+ COSName subtype = (COSName) cos_stream
+ .getDictionaryObject(COSName.SUBTYPE);
+ if (subtype.equals(COSName.IMAGE)) {
+ logger_.debug("XObject Image");
+
+ Matrix ctm = context.getGraphicsState()
+ .getCurrentTransformationMatrix();
+ logger_.debug("ctm = " + ctm);
+
+ Pos[] coordinates = new Pos[] { new Pos(0, 0, 1),
+ new Pos(1, 0, 1), new Pos(0, 1, 1), new Pos(1, 1, 1) };
+
+ Pos[] transformed_coordinates = transtormCoordinates(
+ coordinates, ctm);
+
+ /**********************************************************
+ * pdf-as fix: calculating min and max point of an image to look
+ * where the signature should be placed fix solves problems with
+ * footer and images and placement of the signature in an image
+ * only pdf document
+ **********************************************************/
+
+ float actual_lowest_point = Float.NaN;
+ float actual_starting_point = Float.NaN;
+
+ int pageRotation = page.findRotation();
+ logger_.debug("PageRotation = " + pageRotation);
+ if (pageRotation == 0) {
+ float min_y = findMinY(transformed_coordinates);
+ logger_.debug("min_y = " + min_y);
+ float page_height = page.findMediaBox().getHeight();
+ logger_.debug("page_height = " + page_height);
+
+ actual_lowest_point = page_height - min_y;
+ actual_starting_point = page_height
+ - findMaxY(transformed_coordinates);
+ }
+ if (pageRotation == 90) {
+ float max_x = findMaxX(transformed_coordinates);
+ logger_.debug("max_x = " + max_x);
+ float page_width = page.findMediaBox().getWidth();
+ logger_.debug("page_width = " + page_width);
+
+ actual_lowest_point = max_x;
+ actual_starting_point = findMinX(transformed_coordinates);
+ }
+ if (pageRotation == 180) {
+ float min_y = findMinY(transformed_coordinates);
+ logger_.debug("min_y = " + min_y);
+ float page_height = page.findMediaBox().getHeight();
+ actual_lowest_point = page_height
+ - findMaxY(transformed_coordinates);
+ actual_starting_point = page_height - min_y;
+ }
+ if (pageRotation == 270) {
+ float min_x = findMinX(transformed_coordinates);
+ logger_.debug("min_x = " + min_x);
+
+ float page_width = page.findMediaBox().getWidth();
+ logger_.debug("page_width = " + page_width);
+
+ actual_lowest_point = page_width - min_x;
+ actual_starting_point = page_width
+ - findMaxX(transformed_coordinates);
+ }
+
+ logger_.debug("actual_lowest_point = " + actual_lowest_point);
+
+ if (actual_lowest_point > PDFPage.this.effectivePageHeight
+ && actual_starting_point > PDFPage.this.effectivePageHeight) {
+ logger_.debug("image is below footer_line");
+ return;
+ }
+
+ if (actual_lowest_point > PDFPage.this.max_image_ypos) {
+ PDFPage.this.max_image_ypos = actual_lowest_point;
+ }
+
+ return;
+ }
+
+ if (xobject instanceof PDXObjectForm) {
+ PDXObjectForm form = (PDXObjectForm) xobject;
+ COSStream invoke = (COSStream) form.getCOSObject();
+ PDResources pdResources = form.getResources();
+ PDPage page = context.getCurrentPage();
+ if (pdResources == null) {
+ pdResources = page.findResources();
+ }
+
+ getContext().processSubStream(page, pdResources, invoke);
+ }
+ }
+ }
+
+ public static Pos[] transtormCoordinates(Pos[] coordinates, Matrix m) {
+ Pos[] transformed = new Pos[coordinates.length];
+ for (int i = 0; i < coordinates.length; i++) {
+ transformed[i] = transtormCoordinate(coordinates[i], m);
+ }
+ return transformed;
+ }
+
+ public static Pos transtormCoordinate(Pos pos, Matrix m) {
+ Pos transformed = new Pos();
+ transformed.x = pos.x * m.getValue(0, 0) + pos.y * m.getValue(1, 0)
+ + pos.z * m.getValue(2, 0);
+ transformed.y = pos.x * m.getValue(0, 1) + pos.y * m.getValue(1, 1)
+ + pos.z * m.getValue(2, 1);
+ transformed.z = pos.x * m.getValue(0, 2) + pos.y * m.getValue(1, 2)
+ + pos.z * m.getValue(2, 2);
+
+ logger_.debug(" transformed " + pos + " --> " + transformed);
+ return transformed;
}
- public class MyInvoke extends OperatorProcessor
- {
-
- public void process(PDFOperator operator, List arguments) throws IOException
- {
- COSName name = (COSName) arguments.get(0);
-
- // PDResources res = context.getResources();
-
- Map xobjects = context.getXObjects();
- PDXObject xobject = (PDXObject) xobjects.get(name.getName());
-
- PDStream stream = xobject.getPDStream();
- COSStream cos_stream = stream.getStream();
-
- COSName subtype = (COSName) cos_stream.getDictionaryObject(COSName.SUBTYPE);
- if (subtype.equals(COSName.IMAGE))
- {
- logger_.debug("XObject Image");
-
- Matrix ctm = context.getGraphicsState().getCurrentTransformationMatrix();
- logger_.debug("ctm = " + ctm);
-
- Pos [] coordinates = new Pos [] {
- new Pos(0, 0, 1),
- new Pos(1, 0, 1),
- new Pos(0, 1, 1),
- new Pos(1, 1, 1) };
-
- Pos [] transformed_coordinates = transtormCoordinates(coordinates, ctm);
-
- /**********************************************************
- * pdf-as fix:
- * calculating min and max point of an image to look where
- * the signature should be placed
- * fix solves problems with footer and images and
- * placement of the signature in an image only pdf document
- **********************************************************/
-
- float actual_lowest_point = Float.NaN;
- float actual_starting_point = Float.NaN;
-
- int pageRotation = page.findRotation();
- logger_.debug("PageRotation = " + pageRotation);
- if (pageRotation == 0)
- {
- float min_y = findMinY(transformed_coordinates);
- logger_.debug("min_y = " + min_y);
- float page_height = page.findMediaBox().getHeight();
- logger_.debug("page_height = " + page_height);
-
- actual_lowest_point = page_height - min_y;
- actual_starting_point = page_height - findMaxY(transformed_coordinates);
- }
- if (pageRotation == 90)
- {
- float max_x = findMaxX(transformed_coordinates);
- logger_.debug("max_x = " + max_x);
- float page_width = page.findMediaBox().getWidth();
- logger_.debug("page_width = " + page_width);
-
- actual_lowest_point = max_x;
- actual_starting_point = findMinX(transformed_coordinates);
- }
- if (pageRotation == 180)
- {
- float min_y = findMinY(transformed_coordinates);
- logger_.debug("min_y = " + min_y);
- float page_height = page.findMediaBox().getHeight();
- actual_lowest_point = page_height - findMaxY(transformed_coordinates);
- actual_starting_point = page_height - min_y;
- }
- if (pageRotation == 270)
- {
- float min_x = findMinX(transformed_coordinates);
- logger_.debug("min_x = " + min_x);
-
- float page_width = page.findMediaBox().getWidth();
- logger_.debug("page_width = " + page_width);
-
- actual_lowest_point = page_width - min_x;
- actual_starting_point = page_width - findMaxX(transformed_coordinates);
- }
-
-
- logger_.debug("actual_lowest_point = " + actual_lowest_point);
-
- if (actual_lowest_point > PDFPage.this.effectivePageHeight && actual_starting_point > PDFPage.this.effectivePageHeight)
- {
- logger_.debug("image is below footer_line");
- return;
- }
-
- if (actual_lowest_point > PDFPage.this.max_image_ypos)
- {
- PDFPage.this.max_image_ypos = actual_lowest_point;
- }
-
- return;
- }
-
- if (xobject instanceof PDXObjectForm)
- {
- PDXObjectForm form = (PDXObjectForm) xobject;
- COSStream invoke = (COSStream) form.getCOSObject();
- PDResources pdResources = form.getResources();
- PDPage page = context.getCurrentPage();
- if (pdResources == null)
- {
- pdResources = page.findResources();
- }
-
- getContext().processSubStream(page, pdResources, invoke);
- }
- }
- }
-
- public static Pos [] transtormCoordinates (Pos [] coordinates, Matrix m)
- {
- Pos [] transformed = new Pos [coordinates.length];
- for (int i = 0; i < coordinates.length; i++)
- {
- transformed[i] = transtormCoordinate(coordinates[i], m);
- }
- return transformed;
- }
-
- public static Pos transtormCoordinate (Pos pos, Matrix m)
- {
- Pos transformed = new Pos();
- transformed.x = pos.x * m.getValue(0, 0) + pos.y * m.getValue(1, 0) + pos.z * m.getValue(2, 0);
- transformed.y = pos.x * m.getValue(0, 1) + pos.y * m.getValue(1, 1) + pos.z * m.getValue(2, 1);
- transformed.z = pos.x * m.getValue(0, 2) + pos.y * m.getValue(1, 2) + pos.z * m.getValue(2, 2);
-
- logger_.debug(" transformed " + pos + " --> " + transformed);
- return transformed;
- }
-
- public static float findMinY (Pos [] coordinates)
- {
- float min = Float.POSITIVE_INFINITY;
- for (int i = 0; i < coordinates.length; i++)
- {
- if (coordinates[i].y < min)
- {
- min = coordinates[i].y;
- }
- }
- return min;
- }
+ public static float findMinY(Pos[] coordinates) {
+ float min = Float.POSITIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++) {
+ if (coordinates[i].y < min) {
+ min = coordinates[i].y;
+ }
+ }
+ return min;
+ }
public static float findMaxY(Pos[] coordinates) {
float max = 0;
@@ -495,29 +497,24 @@ public class PDFPage extends PDFTextStripper
return max;
}
- public static float findMaxX (Pos [] coordinates)
- {
- float max = Float.NEGATIVE_INFINITY;
- for (int i = 0; i < coordinates.length; i++)
- {
- if (coordinates[i].x > max)
- {
- max = coordinates[i].x;
- }
- }
- return max;
- }
- public static float findMinX (Pos [] coordinates)
- {
- float min = Float.POSITIVE_INFINITY;
- for (int i = 0; i < coordinates.length; i++)
- {
- if (coordinates[i].x < min)
- {
- min = coordinates[i].x;
- }
- }
- return min;
- }
+ public static float findMaxX(Pos[] coordinates) {
+ float max = Float.NEGATIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++) {
+ if (coordinates[i].x > max) {
+ max = coordinates[i].x;
+ }
+ }
+ return max;
+ }
+
+ public static float findMinX(Pos[] coordinates) {
+ float min = Float.POSITIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++) {
+ if (coordinates[i].x < min) {
+ min = coordinates[i].x;
+ }
+ }
+ return min;
+ }
} \ No newline at end of file