[FOray-commit] SF.net SVN: foray:[11841] trunk/foray
Modular XSL-FO Implementation for Java.
Status: Alpha
Brought to you by:
victormote
|
From: <vic...@us...> - 2021-01-20 14:31:39
|
Revision: 11841
http://sourceforge.net/p/foray/code/11841
Author: victormote
Date: 2021-01-20 14:31:37 +0000 (Wed, 20 Jan 2021)
Log Message:
-----------
1. Break PdfDashPattern into a separate class. 2. Clean up some doc.
Modified Paths:
--------------
trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfGraphicsState.java
trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfTextState.java
trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfContentStream4a.java
trunk/foray/foray-render/src/main/java/org/foray/render/pdf/PdfRenderer.java
Added Paths:
-----------
trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfDashPattern.java
Modified: trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfGraphicsState.java
===================================================================
--- trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfGraphicsState.java 2021-01-20 12:02:48 UTC (rev 11840)
+++ trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfGraphicsState.java 2021-01-20 14:31:37 UTC (rev 11841)
@@ -29,40 +29,60 @@
package org.foray.pdf;
import org.foray.pdf.object.PdfColor4a;
+import org.foray.pdf.object.PdfDashPattern;
import org.axsl.pdf.PdfColor;
import org.axsl.pdf.PdfLineCapStyle;
+import lombok.Getter;
+
/**
- * Represents a PDF Graphics State object. The Graphics State is ordinarily
- * used by PDF viewer applications. Our application is instead a PDF writing
- * application. However keeping track of the Graphics State is useful because
- * it allows us to avoid writing operators that change the Graphics State
- * unnecessarily.
+ * The PDF graphics state.
+ * Tracks the current state of various properties used to render content, such as colors and fonts.
+ * The PDF Reference distinguishes between graphics state parameters that are device-independent and those that are
+ * device-dependent.
+ * While rendering the PDF document, we care only about those that are device-independent, and therefore track only
+ * those in this class.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 4.3."
*/
public class PdfGraphicsState {
- /** Constant for the initial horizontal scaling percentage, that is 100%. */
- public static final float INITIAL_HORIZONTAL_SCALING = 100;
-
- /** The stroke color. */
+ /**
+ * The stroke color.
+ * @return The stroke color.
+ */
+ @Getter
private PdfColor strokeColor;
- /** The "non-stroke" or "other" color. */
+ /**
+ * The "non-stroke" or "other" color.
+ * @return The non-stroke or other color.
+ */
+ @Getter
private PdfColor fillColor;
- /** The text state. */
+ /**
+ * The text state.
+ * @return The text state.
+ */
+ @Getter
private PdfTextState textState = new PdfTextState();
- /** The dash array. */
- private float[] dashArray;
-
- /** The dash phase. */
- private float dashPhase;
-
- /** The line cap style. */
+ /**
+ * The line cap style.
+ * @return The line cap style.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 4.3.2."
+ */
+ @Getter
private PdfLineCapStyle lineCapStyle;
+ /**
+ * The dash pattern.
+ * @return The dash pattern.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 4.3.2."
+ */
+ @Getter
+ private PdfDashPattern dashPattern;
/**
* Create a PdfGraphicsState instance.
@@ -72,41 +92,36 @@
}
/**
- * Creates a new PdfGraphicsState instance, initilializing its values to
- * those of an existing instance.
- * @param existingState The instance whose values should be copied into the
- * new instance.
+ * Creates a new PdfGraphicsState instance, initilializing its values to those of an existing instance.
+ * @param existingState The instance whose values should be copied into the new instance.
*/
public PdfGraphicsState(final PdfGraphicsState existingState) {
this.strokeColor = existingState.strokeColor;
this.fillColor = existingState.fillColor;
- this.dashArray = new float[existingState.dashArray.length];
- System.arraycopy(existingState.dashArray, 0, this.dashArray, 0,
- existingState.dashArray.length);
- this.dashPhase = existingState.dashPhase;
+ this.textState = new PdfTextState(this.textState);
this.lineCapStyle = existingState.lineCapStyle;
-
- this.textState = new PdfTextState(this.textState);
+ this.dashPattern = existingState.dashPattern;
}
/**
- * Resets the Graphics State to its original state, as documented in
- * PDF Reference, 3rd Edition, Section 4.3.
+ * Resets each property in the Graphics State to its initial state.
+ * The initial state values are documented in Table 4.2 in "PDF Reference, Sixth Edition (PDF Version 1.7), Section
+ * 4.3."
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 4.3."
*/
public void reset() {
this.strokeColor = PdfColor4a.BLACK;
this.fillColor = PdfColor4a.BLACK;
this.textState.reset();
- this.dashArray = new float[0];
- this.dashPhase = 0;
this.lineCapStyle = PdfLineCapStyle.BUTT_CAP;
+ this.dashPattern = PdfDashPattern.SOLID_LINE;
}
/**
- * Sets the stroke color (Graphics State holds two colors, one for stroke
- * operations and one for all other operations).
- * @param newStrokeColor The color that stroke operations should now use.
- * @return True if the Graphics State was changed by this operation.
+ * Sets the stroke color.
+ * @param newStrokeColor The new stroke color.
+ * @return True if and only if the Graphics State was changed by this operation.
+ * @see #getStrokeColor()
*/
public boolean setStrokeColor(final PdfColor newStrokeColor) {
if (newStrokeColor == null) {
@@ -121,21 +136,11 @@
}
/**
- * Returns the "stroke" color.
- * @return The stroke color.
+ * Sets the "non-stroke" or "other" or "fill" color.
+ * @param newFillColor The new non-stroke color.
+ * @return True if and only if the Graphics State was changed by this operation.
+ * @see #getFillColor()
*/
- public PdfColor getStrokeColor() {
- return this.strokeColor;
- }
-
- /**
- * Sets the "non-stroke" or "other" color (Graphics State holds two colors,
- * one for stroke operations and one for all other operations). This is the
- * one for all other operations. The name "fill" was chosen because that is
- * the common use for this color.
- * @param newFillColor The color that non-stroke operations should now use.
- * @return True if the Graphics State was changed by this operation.
- */
public boolean setFillColor(final PdfColor newFillColor) {
if (newFillColor == null) {
return false;
@@ -149,76 +154,15 @@
}
/**
- * Returns the "non-stroke" or "other" color.
- * @return The fill color.
+ * Sets the line cap style.
+ * @param newLineCapStyle The new line cap style.
+ * @return True if and only if the Graphics State was changed by this operation.
+ * @see #getLineCapStyle()
*/
- public PdfColor getFillColor() {
- return this.fillColor;
- }
-
- /**
- * Returns the text state.
- * @return The text state.
- */
- public PdfTextState getTextState() {
- return this.textState;
- }
-
- /**
- * Sets the line dash pattern. See PDF Reference, 3rd Edition, Section 4.3.
- * @param newDashArray The new dash array.
- * @param newDashPhase The new dash phase.
- * @return True iff the Graphics State was changed by this operation.
- */
- public boolean setLineDashPattern(final float[] newDashArray,
- final float newDashPhase) {
+ public boolean setLineCapStyle(final PdfLineCapStyle newLineCapStyle) {
boolean anyChange = false;
- anyChange |= setLineDashArray(newDashArray);
- anyChange |= setLineDashPhase(newDashPhase);
- return anyChange;
- }
-
- /**
- * Set the dash array. See PDF Reference, 3rd Edition, Section 4.3.
- * @param newDashArray The new dash array.
- * @return True iff the Graphics State was changed by this operation.
- */
- private boolean setLineDashArray(final float[] newDashArray) {
- /* Null is a valid value for a dash array. If either the new or old
- * value is null, or both, we need to handle those cases before trying
- * to test equality between the two values. */
- if (newDashArray == null) {
- if (this.dashArray == null) {
- // Both are null. Nothing changes.
- return false;
- }
- // One is null, one not. Replace old with new.
- this.dashArray = newDashArray;
- return true;
- }
- if (this.dashArray == null) {
- // One is null, one not. Replace old with new.
- this.dashArray = newDashArray;
- return true;
- }
-
- // Neither is null. Do normal test.
- if (newDashArray.equals(this.dashArray)) {
- return false;
- }
- this.dashArray = newDashArray;
- return true;
- }
-
- /**
- * Set the dash phase. See PDF Reference, 3rd Edition, Section 4.3.
- * @param newDashPhase The new dash phase.
- * @return True iff the Graphics State was changed by this operation.
- */
- private boolean setLineDashPhase(final float newDashPhase) {
- boolean anyChange = false;
- if (newDashPhase != this.dashPhase) {
- this.dashPhase = newDashPhase;
+ if (newLineCapStyle != this.lineCapStyle) {
+ this.lineCapStyle = newLineCapStyle;
anyChange = true;
}
return anyChange;
@@ -225,18 +169,18 @@
}
/**
- * Sets the line cap style. See PDF Reference, 3rd Edition, Section 4.3.
- * @param newLineCapStyle The new line cap style.
- * @return True iff the Graphics State was changed by this operation.
+ * Sets the line dash pattern.
+ * @param newDashPattern The new dash pattern.
+ * @return True if and only if the Graphics State was changed by this operation.
+ * @see #getDashPattern()
*/
- public boolean setLineCapStyle(final PdfLineCapStyle newLineCapStyle) {
+ public boolean setLineDashPattern(final PdfDashPattern newDashPattern) {
boolean anyChange = false;
- if (newLineCapStyle != this.lineCapStyle) {
- this.lineCapStyle = newLineCapStyle;
+ if (! this.dashPattern.equals(newDashPattern)) {
+ this.dashPattern = newDashPattern;
anyChange = true;
}
return anyChange;
}
-
}
Modified: trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfTextState.java
===================================================================
--- trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfTextState.java 2021-01-20 12:02:48 UTC (rev 11840)
+++ trunk/foray/foray-pdf/src/main/java/org/foray/pdf/PdfTextState.java 2021-01-20 14:31:37 UTC (rev 11841)
@@ -31,6 +31,8 @@
import org.axsl.pdf.PdfFont;
import org.axsl.pdf.PdfTextRenderingMode;
+import lombok.Getter;
+
/**
* The text state for the PDF graphics state.
* @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2."
@@ -37,31 +39,77 @@
*/
public class PdfTextState {
- /** The character spacing, in points. */
+ /** Constant for the initial horizontal scaling percentage, that is 100%. */
+ public static final float INITIAL_HORIZONTAL_SCALING = 100;
+
+ /**
+ * The character spacing, expressed in unscaled text space units.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.1."
+ * @return The character spacing.
+ */
+ @Getter
private float characterSpacing;
- /** The word spacing, in points. */
+ /**
+ * The word spacing, expressed in unscaled text space units.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.2."
+ * @return The word spacing.
+ */
+ @Getter
private float wordSpacing;
- /** The horizontal scaling percentage. */
+ /**
+ * The horizontal scaling, a percentage.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.3."
+ * @return The horizontal scaling.
+ */
+ @Getter
private float horizontalScaling;
- /** The text leading, expressed in unscaled text units. */
+ /**
+ * The text leading, expressed in unscaled text units.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.4."
+ * @return The text leading.
+ */
+ @Getter
private float leading;
- /** The font. */
+ /**
+ * The font.
+ * @return The font.
+ */
+ @Getter
private PdfFont font;
- /** The font size. */
+ /**
+ * The font size.
+ * @return The font size.
+ */
+ @Getter
private float fontSize;
- /** The text rendering mode. */
+ /**
+ * The text rendering mode.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.5."
+ * @return The text rendering mode.
+ */
+ @Getter
private PdfTextRenderingMode textRenderingMode;
- /** The text rise, expressed in unscaled text space units. */
+ /**
+ * The text rise, expressed in unscaled text space units.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.6."
+ * @return The text rise.
+ */
+ @Getter
private float textRise;
- /** The text knockout flag. */
+ /**
+ * The text knockout flag.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2.7."
+ * @return The text knockout.
+ */
+ @Getter
private boolean textKnockout;
/**
@@ -72,10 +120,8 @@
}
/**
- * Creates a new PdfGraphicsState instance, initilializing its values to
- * those of an existing instance.
- * @param existingState The instance whose values should be copied into the
- * new instance.
+ * Creates a new instance, initilializing its values to those of an existing instance.
+ * @param existingState The instance whose values should be copied into the new instance.
*/
public PdfTextState(final PdfTextState existingState) {
this.characterSpacing = existingState.characterSpacing;
@@ -90,13 +136,15 @@
}
/**
- * Resets the Graphics State to its original state, as documented in
- * PDF Reference, 3rd Edition, Section 4.3.
+ * Resets the text state parameters to their initial values.
+ * The initial values, except for font and fontSize are documented at Table 5.2 in "PDF Reference, Sixth Edition
+ * (PDV Version 1.7), Section 5.2".
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 5.2."
*/
public void reset() {
this.characterSpacing = 0;
this.wordSpacing = 0;
- this.horizontalScaling = PdfGraphicsState.INITIAL_HORIZONTAL_SCALING;
+ this.horizontalScaling = PdfTextState.INITIAL_HORIZONTAL_SCALING;
this.leading = 0;
this.font = null;
this.fontSize = -1;
@@ -107,8 +155,9 @@
/**
* Sets the character spacing.
- * @param newCharacterSpacing The new character spacing, in points.
- * @return True iff the character spacing was changed.
+ * @param newCharacterSpacing The new character spacing, expressed in unscaled text space units.
+ * @return True if and only if the character spacing was changed.
+ * @see #getCharacterSpacing()
*/
public boolean setCharacterSpacing(final float newCharacterSpacing) {
boolean anyChange = false;
@@ -121,8 +170,9 @@
/**
* Sets the word spacing.
- * @param newWordSpacing The new word spacing, in points.
- * @return True iff the word spacing was changed.
+ * @param newWordSpacing The new word spacing, expressed unscaled text space units.
+ * @return True if and only if the word spacing was changed.
+ * @see #getWordSpacing()
*/
public boolean setWordSpacing(final float newWordSpacing) {
boolean anyChange = false;
@@ -134,9 +184,25 @@
}
/**
+ * Sets the horizontal scaling.
+ * @param newHorizontalScaling The new horizontal scaling, expressed as a percentage.
+ * @return True if and only if the horizontal scaling was changed.
+ * @see #getHorizontalScaling()
+ */
+ public boolean setHorizontalScaling(final float newHorizontalScaling) {
+ boolean anyChange = false;
+ if (newHorizontalScaling != this.horizontalScaling) {
+ this.horizontalScaling = newHorizontalScaling;
+ anyChange = true;
+ }
+ return anyChange;
+ }
+
+ /**
* Sets the font.
* @param newFont The new font.
- * @return True iff the font was changed.
+ * @return True if and only if the font was changed.
+ * @see #getFont()
*/
public boolean setFont(final PdfFont newFont) {
boolean anyChange = false;
@@ -148,18 +214,11 @@
}
/**
- * Returns the font.
- * @return The font.
+ * Sets the font size.
+ * @param newFontSize The new font size, in points.
+ * @return True if and only if the font size was changed.
+ * @see #getFontSize()
*/
- public PdfFont getFont() {
- return this.font;
- }
-
- /**
- * Sets the font-size.
- * @param newFontSize The new font-size, in points.
- * @return True iff the font-size was changed.
- */
public boolean setFontSize(final float newFontSize) {
boolean anyChange = false;
if (newFontSize != this.fontSize) {
@@ -170,32 +229,10 @@
}
/**
- * Return the font-size.
- * @return The font-size.
- */
- public float getFontSize() {
- return this.fontSize;
- }
-
- /**
- * Sets the horizontal scaling. See PDF Reference, 3rd Edition, Section
- * 5.2.3.
- * @param newHorizontalScaling The new horizontal scaling percentage.
- * @return True iff the Graphics State was changed by this operation.
- */
- public boolean setHorizontalScaling(final float newHorizontalScaling) {
- boolean anyChange = false;
- if (newHorizontalScaling != this.horizontalScaling) {
- this.horizontalScaling = newHorizontalScaling;
- anyChange = true;
- }
- return anyChange;
- }
-
- /**
* Sets the text leading. See PDF Reference, 3rd Edition, Section 5.2.4.
* @param newLeading The new leading value.
- * @return True iff the Graphics State was changed by this operation.
+ * @return True if and only if the text leading was changed by this operation.
+ * @see #getLeading()
*/
public boolean setLeading(final float newLeading) {
boolean anyChange = false;
@@ -210,7 +247,8 @@
* Sets the text rendering mode. See PDF Reference, 3rd Edition, Section
* 5.2.5.
* @param newTextRenderingMode The new text rendering mode.
- * @return True iff the Graphics State was changed by this operation.
+ * @return True if and only if the text rendering mode was changed by this operation.
+ * @see #getTextRenderingMode()
*/
public boolean setTextRenderingMode(
final PdfTextRenderingMode newTextRenderingMode) {
@@ -225,7 +263,8 @@
/**
* Sets the text rise. See PDF Reference, 3rd Edition, Section 5.2.6.
* @param newTextRise The new text rise value.
- * @return True iff the Graphics State was changed by this operation.
+ * @return True if and only if the text rise was changed by this operation.
+ * @see #getTextRise()
*/
public boolean setTextRise(final float newTextRise) {
boolean anyChange = false;
@@ -237,10 +276,10 @@
}
/**
- * Sets the text knockout flag. See PDF Reference, 3rd Edition, Section
- * 5.2.7.
+ * Sets the text knockout flag.
* @param newTextKnockout The new text rise value.
- * @return True iff the Graphics State was changed by this operation.
+ * @return True if and only if the text knockout was changed by this operation.
+ * @see #isTextKnockout()
*/
public boolean setTextKnockout(final boolean newTextKnockout) {
boolean anyChange = false;
@@ -249,7 +288,6 @@
anyChange = true;
}
return anyChange;
-
}
}
Modified: trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfContentStream4a.java
===================================================================
--- trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfContentStream4a.java 2021-01-20 12:02:48 UTC (rev 11840)
+++ trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfContentStream4a.java 2021-01-20 14:31:37 UTC (rev 11841)
@@ -318,7 +318,8 @@
@Override
public void setDashPattern(final float[] dashArray, final float dashPhase) throws PdfException {
- if (! this.getCurrentGraphicsState().setLineDashPattern(dashArray, dashPhase)) {
+ final PdfDashPattern newDashPattern = new PdfDashPattern(dashArray, dashPhase);
+ if (! this.getCurrentGraphicsState().setLineDashPattern(newDashPattern)) {
// Nothing needs to change.
return;
}
Added: trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfDashPattern.java
===================================================================
--- trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfDashPattern.java (rev 0)
+++ trunk/foray/foray-pdf/src/main/java/org/foray/pdf/object/PdfDashPattern.java 2021-01-20 14:31:37 UTC (rev 11841)
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2021 The FOray Project.
+ * http://www.foray.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This work is in part derived from the following work(s), used with the
+ * permission of the licensor:
+ * Apache FOP, licensed by the Apache Software Foundation
+ *
+ */
+
+/*
+ * $LastChangedRevision$
+ * $LastChangedDate$
+ * $LastChangedBy$
+ */
+
+package org.foray.pdf.object;
+
+import lombok.Getter;
+
+/**
+ * A PDF Line Dash Pattern.
+ * Instances of this class are immutable.
+ * @see "PDF Reference, Sixth Edition (PDF Version 1.7), Section 4.3.2."
+ */
+public class PdfDashPattern {
+
+ /** Dash pattern that will draw a solid, unbroken line. */
+ public static final PdfDashPattern SOLID_LINE = new PdfDashPattern(new float[0], 0);
+
+ /** Constant used to designate a "dotted" dash pattern. */
+ public static final PdfDashPattern DOTTED = new PdfDashPattern(new float[] {1, 3}, 0);
+
+ /** Constant used to designate a "dashed" dash pattern. */
+ public static final PdfDashPattern DASHED = new PdfDashPattern(new float[] {3, 3}, 0);
+
+ /**
+ * The dash array.
+ * @return The dash array.
+ */
+ @Getter
+ private float[] dashArray;
+
+ /**
+ * The dash phase.
+ * @return The dash phase.
+ */
+ @Getter
+ private float dashPhase;
+
+ public PdfDashPattern(final float[] dashArray, final float dashPhase) {
+ this.dashArray = new float[dashArray.length];
+ System.arraycopy(dashArray, 0, this.dashArray, 0, dashArray.length);
+ this.dashPhase = dashPhase;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (! (other instanceof PdfDashPattern)) {
+ return false;
+ }
+ final PdfDashPattern otherDashPattern = (PdfDashPattern) other;
+ if (! this.dashArray.equals(otherDashPattern.dashArray)) {
+ return false;
+ }
+ if (this.dashPhase != otherDashPattern.dashPhase) {
+ return false;
+ }
+ return true;
+ }
+
+}
Modified: trunk/foray/foray-render/src/main/java/org/foray/render/pdf/PdfRenderer.java
===================================================================
--- trunk/foray/foray-render/src/main/java/org/foray/render/pdf/PdfRenderer.java 2021-01-20 12:02:48 UTC (rev 11840)
+++ trunk/foray/foray-render/src/main/java/org/foray/render/pdf/PdfRenderer.java 2021-01-20 14:31:37 UTC (rev 11841)
@@ -95,6 +95,9 @@
*/
public class PdfRenderer extends PrintRenderer {
+ /** Constant used to designate a solid dash pattern. */
+ private static final float[] DASHPATTERN_SOLID = new float[] {};
+
/** Constant used to designate a "dotted" dash pattern. */
private static final float[] DASHPATTERN_DOTTED = new float[] {1, 3};
@@ -232,7 +235,7 @@
final float[] dashArray;
if (ruleStyle == null) {
- dashArray = null;
+ dashArray = PdfRenderer.DASHPATTERN_SOLID;
} else {
switch (ruleStyle) {
case DASHED:
@@ -242,7 +245,7 @@
dashArray = PdfRenderer.DASHPATTERN_DOTTED;
break;
default:
- dashArray = null;
+ dashArray = PdfRenderer.DASHPATTERN_SOLID;
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|