Annotation(PdfName.STAMP) missed after flattened by itextsharp5.5.13.1.
I have two pdfs. One does work, the other does not work.
Any ideas will be appreciated.
The code is following
string outFile = inputFile + "_f.pdf";
using (PdfReader pdfReader = new PdfReader(inputFileName))
{
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None))
//pdfStamper.FormFlattening = true;
//pdfStamper.FreeTextFlattening = true;
pdfStamper.AnnotationFlattening = true;
//pdfStamper.AcroFields.GenerateAppearances = true;
}
The cause is a bug in iTextSharp, annotation flattening does not correctly calculate the position for the flattened annotation if its bounding box does not use the origin as lower left corner.
If you look at the code of PdfStamperImp.FlattenAnnotations(bool), you'll quickly realize that the calculations in the if (app != null) block only make sense if the bounding box is located at the origin or no scaling is necessary for fitting the appearance bounding box into the annotation rectangle.
(As often the lower left corner of the bounding box is the origin, this does not pop up often.)
Thus, for flattening such annotations you have to use a different method for flattening, e.g. like this:
using (PdfReader pdfReader = new PdfReader(inputFileName))
{
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None));
ImprovedAnnotationFlattening(pdfStamper);
pdfStamper.Close();
}
with these helper methods:
void ImprovedAnnotationFlattening(PdfStamper pdfStamper)
{
double[] DEFAULT_MATRIX = { 1, 0, 0, 1, 0, 0 };
PdfReader reader = pdfStamper.Reader;
for (int page = 1; page <= reader.NumberOfPages; ++page)
{
PdfDictionary pageDic = reader.GetPageN(page);
PdfArray annots = pageDic.GetAsArray(PdfName.ANNOTS);
if (annots == null)
continue;
for (int idx = 0; idx < annots.Size; ++idx)
{
PdfObject annoto = annots.GetDirectObject(idx);
if (!(annoto is PdfDictionary))
continue;
PdfDictionary annDic = (PdfDictionary)annoto;
PdfNumber ff = annDic.GetAsNumber(PdfName.F);
int flags = ff != null ? ff.IntValue : 0;
if ((flags & PdfFormField.FLAGS_PRINT) == 0 || (flags & PdfFormField.FLAGS_HIDDEN) != 0)
continue;
PdfObject obj1 = annDic.Get(PdfName.AP);
if (obj1 == null)
continue;
PdfDictionary appDic = obj1 is PdfIndirectReference
? (PdfDictionary)PdfReader.GetPdfObject(obj1)
: (PdfDictionary)obj1;
PdfObject obj = appDic.Get(PdfName.N);
PdfStream objDict = appDic.GetAsStream(PdfName.N);
if (objDict != null)
{
Rectangle rect = PdfReader.GetNormalizedRectangle(annDic.GetAsArray(PdfName.RECT));
Rectangle bbox = PdfReader.GetNormalizedRectangle(objDict.GetAsArray(PdfName.BBOX));
PdfContentByte cb = pdfStamper.GetOverContent(page);
cb.SetLiteral("Q ");
PdfArray matrixArray = objDict.GetAsArray(PdfName.MATRIX);
double[] matrix = matrixArray != null ? matrixArray.AsDoubleArray() : DEFAULT_MATRIX;
AffineTransform transform = new AffineTransform(matrix);
double[] bboxCorners = { bbox.Left, bbox.Bottom, bbox.Right, bbox.Bottom, bbox.Right, bbox.Top, bbox.Left, bbox.Top };
transform.Transform(bboxCorners, 0, bboxCorners, 0, 4);
double minX = Min(bboxCorners, 0, 2);
double maxX = Max(bboxCorners, 0, 2);
double minY = Min(bboxCorners, 1, 2);
double maxY = Max(bboxCorners, 1, 2);
transform.preConcatenate(AffineTransform.GetTranslateInstance(-minX, -minY));
transform.preConcatenate(AffineTransform.GetScaleInstance(rect.Width/(maxX-minX), rect.Height/(maxY-minY)));
transform.preConcatenate(AffineTransform.GetTranslateInstance(rect.Left, rect.Bottom));
transform.GetMatrix(matrix);
cb.AddFormXObj(objDict, GenerateName(), matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
cb.SetLiteral("q ");
annots.Remove(idx);
--idx;
}
}
}
}
double Min(double[] array, int start, int step)
{
double result = array[start];
for (int i = start + step; i < array.Length; i+=step)
{
result = Math.Min(result, array[i]);
}
return result;
}
double Max(double[] array, int start, int step)
{
double result = array[start];
for (int i = start + step; i < array.Length; i += step)
{
result = Math.Max(result, array[i]);
}
return result;
}
PdfName GenerateName()
{
PdfName name = new PdfName("XXX" + formXObjectsCounter);
++formXObjectsCounter;
return name;
}
int formXObjectsCounter = 4711;
Beware: I just wrote these methods (copying as much as possible from the original flattening code) and only tested with your example files. Some border conditions might still have to be considered for general use. In particular I did not do all relevant null or 0 tests. Also I did not attempt to support proper tagging.
Related
I have a logic to export avery label pdf. The logic exports the pdf with labels properly but when i print that pdf, the page size measurements (Page properties) that i pass isn't matching with the printed page.
Page Properties
Width="48.5" Height="25.4" HorizontalGapWidth="0" VerticalGapHeight="0" PageMarginTop="21" PageMarginBottom="21" PageMarginLeft="8" PageMarginRight="8" PageSize="A4" LabelsPerRow="4" LabelRowsPerPage="10"
The above property values are converted equivalent to point values first before applied.
Convert to point
private float mmToPoint(double mm)
{
return (float)((mm / 25.4) * 72);
}
Logic
public Stream SecLabelType(LabelProp _label)
{
List<LabelModelClass> Model = new List<LabelModelClass>();
Model = RetModel(_label);
bool IncludeLabelBorders = false;
FontFactory.RegisterDirectories();
Rectangle pageSize;
switch (_label.PageSize)
{
case "A4":
pageSize = iTextSharp.text.PageSize.A4;
break;
default:
pageSize = iTextSharp.text.PageSize.A4;
break;
}
var doc = new Document(pageSize,
_label.PageMarginLeft,
_label.PageMarginRight,
_label.PageMarginTop,
_label.PageMarginBottom);
var output = new MemoryStream();
var writer = PdfWriter.GetInstance(doc, output);
writer.CloseStream = false;
doc.Open();
var numOfCols = _label.LabelsPerRow + (_label.LabelsPerRow - 1);
var tbl = new PdfPTable(numOfCols);
var colWidths = new List<float>();
for (int i = 1; i <= numOfCols; i++)
{
if (i % 2 > 0)
{
colWidths.Add(_label.Width);
}
else
{
colWidths.Add(_label.HorizontalGapWidth);
}
}
var w = iTextSharp.text.PageSize.A4.Width - (doc.LeftMargin + doc.RightMargin);
var h = iTextSharp.text.PageSize.A4.Height - (doc.TopMargin + doc.BottomMargin);
var size = new iTextSharp.text.Rectangle(w, h);
tbl.SetWidthPercentage(colWidths.ToArray(), size);
//var val = System.IO.File.ReadLines("C:\\Users\\lenovo\\Desktop\\test stock\\testing3.txt").ToArray();
//var ItemNoArr = Model.Select(ds => ds.ItemNo).ToArray();
//string Header = Model.Select(ds => ds.Header).FirstOrDefault();
int cnt = 0;
bool b = false;
int iAddRows = 1;
for (int iRow = 0; iRow < ((Model.Count() / _label.LabelsPerRow) + iAddRows); iRow++)
{
var rowCells = new List<PdfPCell>();
for (int iCol = 1; iCol <= numOfCols; iCol++)
{
if (Model.Count() > cnt)
{
if (iCol % 2 > 0)
{
var cellContent = new Phrase();
if (((iRow + 1) >= _label.StartRow && (iCol) >= (_label.StartColumn + (_label.StartColumn - 1))) || b)
{
b = true;
try
{
var StrArr = _label.SpineLblFormat.Split('|');
foreach (var x in StrArr)
{
string Value = "";
if (x.Contains(","))
{
var StrCommaArr = x.Split(',');
foreach (var y in StrCommaArr)
{
if (y != "")
{
Value = ChunckText(cnt, Model, y, Value);
}
}
if (Value != null && Value.Replace(" ", "") != "")
{
cellContent.Add(new Paragraph(Value));
cellContent.Add(new Paragraph("\n"));
}
}
else
{
Value = ChunckText(cnt, Model, x, Value);
if (Value != null && Value.Replace(" ", "") != "")
{
cellContent.Add(new Paragraph(Value));
cellContent.Add(new Paragraph("\n"));
}
}
}
}
catch (Exception e)
{
var fontHeader1 = FontFactory.GetFont("Verdana", BaseFont.CP1250, true, 6, 0);
cellContent.Add(new Chunk("NA", fontHeader1));
}
cnt += 1;
}
else
iAddRows += 1;
var cell = new PdfPCell(cellContent);
cell.FixedHeight = _label.Height;
cell.HorizontalAlignment = Element.ALIGN_LEFT;
cell.Border = IncludeLabelBorders ? Rectangle.BOX : Rectangle.NO_BORDER;
rowCells.Add(cell);
}
else
{
var gapCell = new PdfPCell();
gapCell.FixedHeight = _label.Height;
gapCell.Border = Rectangle.NO_BORDER;
rowCells.Add(gapCell);
}
}
else
{
var gapCell = new PdfPCell();
gapCell.FixedHeight = _label.Height;
gapCell.Border = Rectangle.NO_BORDER;
rowCells.Add(gapCell);
}
}
tbl.Rows.Add(new PdfPRow(rowCells.ToArray()));
_label.LabelRowsPerPage = _label.LabelRowsPerPage == null ? 0 : _label.LabelRowsPerPage;
if ((iRow + 1) < _label.LabelRowsPerPage && _label.VerticalGapHeight > 0)
{
tbl.Rows.Add(CreateGapRow(numOfCols, _label));
}
}
doc.Add(tbl);
doc.Close();
output.Position = 0;
return output;
}
private PdfPRow CreateGapRow(int numOfCols, LabelProp _label)
{
var cells = new List<PdfPCell>();
for (int i = 0; i < numOfCols; i++)
{
var cell = new PdfPCell();
cell.FixedHeight = _label.VerticalGapHeight;
cell.Border = Rectangle.NO_BORDER;
cells.Add(cell);
}
return new PdfPRow(cells.ToArray());
}
A PDF document may have very accurate measurements, but then those measurements get screwed up because the page is scaled during the printing process. That is a common problem: different printers will use different scaling factors with different results when you print the document using different printers.
How to avoid this?
In the print dialog of Adobe Reader, you can choose how the printer should behave:
By default, the printer will try to "Fit" the content on the page, but as not every printer can physically use the full page size (due to hardware limitations), there's a high chance the printer will scale the page down if you use "Fit".
It's better to choose the option "Actual size". The downside of using this option is that some content may get lost because it's too close to the border of the page in an area that physically can't be reached by the printer, but the advantage is that the measurements will be preserved.
You can set this option programmatically in your document by telling the document it shouldn't scale:
writer.AddViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
See How to set initial view properties? for more info about viewer preferences.
I have done the multiple file up-loader and save the images in database as (byte)
while retrieving those all images memory out of exception was thrown so i want to reduce the size of uploaded multiple files through pragmatically (c#,asp.net)
with that i have problem with reducing the sizes of multiple images using file up-loader in .net before saving to database
protected void DataUploading()
{
try
{
int count;
if (uploader.HasFile)
{
string s = System.IO.Path.GetExtension(uploader.FileName);
if ((s == ".JPG") || (s == ".JPEG") || (s == ".PNG") || (s == ".BMP") || (s == ".jpg") || (s == ".jpeg") || (s == ".png") || (s == ".bmp") || (s == ".jpe"))
{
count = ReferrenceCount();
HttpFileCollection fileCollection = Request.Files;
if (count == 0)
{
count = 0;
}
for (int i = 0; i < fileCollection.Count; i++)
{
HttpPostedFile uploadfile = fileCollection[i];
int fileSize = uploadfile.ContentLength;
if (fileSize <= 31457280)//3145728-5242880
{
string fileName = Path.GetFileName(uploadfile.FileName);
if (uploadfile.ContentLength > 0)
{
string contentType = uploader.PostedFile.ContentType;
using (Stream fs = uploadfile.InputStream)
{
using (BinaryReader br = new BinaryReader(fs))
{
byte[] bytes = br.ReadBytes((Int32)fs.Length);
byte[] newbytes = reducesize(uploadfile);
imagelist.Add(bytes);
refcounts.Add(count);
count += 1;
}
}
}
}
else
{
//image compression code to be written here
ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "alertMessage", "alert('FILE SIZE SHOULD BE LIMIT TO 5MB')", true);
}
}
}
else
{
ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "alertMessage", "alert('ONLY JPEG,BMP,PNG ARE ALLOWED')", true);
}
for (int ij = 0; ij < imagelist.Count; ij++)
{
using (con = new SqlConnection(constr))
{
string query = string.Empty;
query = "INSERT INTO INVENTORY_IMAGES(CLAIM_NUMBER,PHOTOS,REF_NO,UPDATEDATE,MODIFIEDDATE,MODIFIEDBY,CHASSIS) values (#Name, #Data,#REF,#DATE,#datetime,#user,#chassis)";
using (cmd = new SqlCommand(query))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#Name", txtclaimnumber.Text);
cmd.Parameters.AddWithValue("#Data", imagelist[ij]);
cmd.Parameters.AddWithValue("#REF", refcounts[ij].ToString());
cmd.Parameters.AddWithValue("#DATE", DateTime.Now.ToString("dd/MM/yyyy"));
cmd.Parameters.AddWithValue("#datetime", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
cmd.Parameters.AddWithValue("#user", Session["user"].ToString());
cmd.Parameters.AddWithValue("#chassis", txtchasisno.Text);
con.Open();
cmd.ExecuteNonQuery();//'"+DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")+"','"+Session["user"].ToString()+"','"++"'
}
}
}
imagelist.Clear();
refcounts.Clear();
}
else
{
ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "alertMessage", "alert('PLEASE SELECT A FILE')", true);
}
}
catch (Exception ee)
{
WriteLog(ee.Message);
throw ee;
}
}
private byte[] reducesize(HttpPostedFile HttpPostedFiles)
{
//variable to store the image content
//the size of the variable is initialized to the file content length
byte[] imageBytes = new byte[HttpPostedFiles.ContentLength];
//Gets the underlying System.Web.HttpPostedFile object for a file that is uploaded
//by using the System.Web.UI.WebControls.FileUpload control.
//HttpPostedFile uploadImage = fileUploadS.PostedFile;
//read the image stream from the post and store it in imageBytes
HttpPostedFiles.InputStream.Read(imageBytes, 0, (int)HttpPostedFiles.ContentLength);
//resize image to a thumbnail
MemoryStream thumbnailPhotoStream = ResizeImage(HttpPostedFiles);
byte[] thumbnailImageBytes = thumbnailPhotoStream.ToArray();
return thumbnailImageBytes;
//end
}
private MemoryStream ResizeImage(HttpPostedFile HttpPostedFiless)
{
try
{
Bitmap originalBMP = new Bitmap(HttpPostedFiless.InputStream);
// Create a bitmap with the uploaded file content
//Bitmap originalBMP = new Bitmap(inputContent);
// get the original dimensions
int origWidth = originalBMP.Width;
int origHeight = originalBMP.Height;
//calculate the current aspect ratio
int aspectRatio = origWidth / origHeight;
//if the aspect ration is less than 0, default to 1
if (aspectRatio <= 0)
aspectRatio = 1;
//new width of the thumbnail image
int newWidth = 100;
//calculate the height based on the aspect ratio
int newHeight = newWidth / aspectRatio;
// Create a new bitmap to store the new image
Bitmap newBMP = new Bitmap(originalBMP, newWidth, newHeight);
// Create a graphic based on the new bitmap
Graphics graphics = Graphics.FromImage(newBMP);
// Set the properties for the new graphic file
graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the new graphic based on the resized bitmap
graphics.DrawImage(originalBMP, 0, 0, newWidth, newHeight);
//save the bitmap into a memory stream
System.IO.MemoryStream stream = new System.IO.MemoryStream();
newBMP.Save(stream, GetImageFormat(System.IO.Path.GetExtension(HttpPostedFiless.FileName)));
// dispose drawing objects
originalBMP.Dispose();
newBMP.Dispose();
graphics.Dispose();
return stream;
}
catch (Exception ee)
{
WriteLog(ee.Message);
throw;
}
}
private System.Drawing.Imaging.ImageFormat GetImageFormat(string extension)
{
switch (extension.ToLower())
{
case "jpg":
return System.Drawing.Imaging.ImageFormat.Jpeg;
case "bmp":
return System.Drawing.Imaging.ImageFormat.Bmp;
case "png":
return System.Drawing.Imaging.ImageFormat.Png;
}
return System.Drawing.Imaging.ImageFormat.Jpeg;
}
I am using below function to generate pdf using itextsharp. The problem is that the document generated has the Acro Fields as flattened (not editable by user) .
Is is possible to generate/merge pdf's using itextsharp without acroform fields getting flattned?
protected override Document ConvertToPdf(XElement rptXData, Document doc, PdfWriter writer)
{
var rptcfg = ReportContext<ReportConfig>.Me;
var pageHeaderFooter = ReportContext<PageHeaderFooter>.Me;
#region values from xml
var showheader = rptXData.Attribute("showheader").BooleanValue();
var showfooter = rptXData.Attribute("showfooter").BooleanValue();
var src = rptXData.Attribute("src").StringValue();
if (src != "") src = System.IO.Path.Combine(rptcfg.ResourceRootPath, src);
#endregion
pageHeaderFooter.IsShowHeader = showheader;
pageHeaderFooter.IsShowFooter = showfooter;
var content = writer.DirectContent;
if (src == "" || !System.IO.File.Exists(src))
{
ReportTable wrapper = new ReportTable(new float[] { DocWidth }, true);
if (System.IO.Path.GetFileNameWithoutExtension(src).ToUpper() == "BLANKPAGE")
wrapper.AddCellEx("");
else
wrapper.AddCellEx("Error in merging outside pages: Page " + src + " does not exist");
doc.Add(wrapper);
return doc;
}
try
{
// Create pdf reader
PdfReader reader = new PdfReader(src);
int numberOfPages = reader.NumberOfPages;
// Iterate through all pages
for (int currentPageIndex = 1; currentPageIndex <= numberOfPages; currentPageIndex++)
{
// Determine page size for the current page
doc.SetPageSize(reader.GetPageSizeWithRotation(currentPageIndex));
// Create page and get the imported page
doc.NewPage();
PdfImportedPage importedPage = writer.GetImportedPage(reader, currentPageIndex);
// Determine page orientation
int pageOrientation = reader.GetPageRotation(currentPageIndex);
if ((pageOrientation == 90) || (pageOrientation == 270))
{
content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
reader.GetPageSizeWithRotation(currentPageIndex).Height);
}
else if (pageOrientation == 180) //NEW JUN13; GF-357
{
content.AddTemplate(importedPage, -1f, 0, 0, -1f,
reader.GetPageSizeWithRotation(currentPageIndex).Width,
reader.GetPageSizeWithRotation(currentPageIndex).Height);
}
else
{
content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
}
}
}
catch (Exception exception)
{
throw new Exception("PDF Merging Exception:", exception);
}
return doc;
}
I have 3 pdfs templates that i Fill out and save to a memorystream them merge them together into 1 pdf into a memorystream. The resulting merged pdf is non flattenned. I tried reading the merged pdf and flattening it but it say "pdf header signature not found". Is there a way to flattened merged pdf from a memorystream.
this is what I tried so far without success.
var mergedPdf = MergePDFs(AllPdfs);
MemoryStream flattenedMergedPdf = new MemoryStream();
PdfReader Finalpdf = new PdfReader(mergedPdf);
PdfStamper StampFinalMerged = new PdfStamper(Finalpdf,flattenedMergedPdf);
StampFinalMerged.FormFlattening=true;
return flattenedMergedPdf;
the method i use to merge the pdfs(found on SO) is below:
public MemoryStream MergePDFs(List<byte[]> pdfFiles)
{
if (pdfFiles.Count > 1)
{
PdfReader finalPdf;
Document pdfContainer;
PdfCopy pdfCopy;
MemoryStream msFinalPdf = new MemoryStream();
finalPdf = new PdfReader(pdfFiles[0]);
pdfContainer = new Document();
pdfCopy = new PdfSmartCopy(pdfContainer, msFinalPdf);
pdfContainer.Open();
for (int k = 0; k < pdfFiles.Count; k++)
{
finalPdf = new PdfReader(pdfFiles[k]);
for (int i = 1; i < finalPdf.NumberOfPages + 1; i++)
{
((PdfSmartCopy)pdfCopy).AddPage(pdfCopy.GetImportedPage(finalPdf, i));
}
pdfCopy.FreeReader(finalPdf);
}
//tried this here but no success
// PdfReader FinalRead = new PdfReader(finalPdf);
//MemoryStream FinalMS = new MemoryStream();
//var finalStamper = new PdfStamper(FinalRead, FinalMS);
//finalStamper.FormFlattening = true;
pdfCopy.CloseStream = false;
pdfCopy.Close();
finalPdf.Close();
//return FinalMS
return msFinalPdf;
}
Any help will be much appreciated. Thanks
You can try another approach to flatten PDF templates first then merge them into a final PDF file:
string newFile = "... path to the merged pdf file ...";
string[] pdfTemplates = new string[3];
string pdfTemplates[0] = "... path to the 1st pdf template ...";
string pdfTemplates[1] = "... path to the 2nd pdf template ...";
string pdfTemplates[2] = "... path to the 3rd pdf template ...";
string[] pdfFiles = new string[3];
string pdfFiles[0] = "... path to the 1st temp pdf file ...";
string pdfFiles[1] = "... path to the 2nd temp pdf file ...";
string pdfFiles[2] = "... path to the 3rd temp pdf file ...";
for (int i = 0; i < 3; i++)
{
PdfReader pdfReader = new PdfReader(pdfTemplates[i]);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(pdfFiles[i], FileMode.Create));
pdfStamper.FormFlattening = true;
pdfStamper.Close();
}
MergeFiles(newFile, pdfFiles);
for (int i = 0; i < 3; i++)
{
FileInfo fi = new FileInfo(pdfFiles[i]);
fi.Delete();
}
private void MergeFiles(string destinationFile, string[] sourceFiles)
{
try
{
int f = 0;
PdfReader reader = new PdfReader(sourceFiles[f]);
int n = reader.NumberOfPages;
Document document = new Document(reader.GetPageSizeWithRotation(1));
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(destinationFile, FileMode.Create));
document.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page;
int rotation;
while (f < sourceFiles.Length)
{
int i = 0;
while (i < n)
{
i++;
document.SetPageSize(reader.GetPageSizeWithRotation(i));
document.NewPage();
page = writer.GetImportedPage(reader, i);
rotation = reader.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb.AddTemplate(page, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(i).Height);
}
else
{
cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
}
f++;
if (f < sourceFiles.Length)
{
reader = new PdfReader(sourceFiles[f]);
n = reader.NumberOfPages;
}
}
document.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
I'm creating a mobius strip in Java3D. I've got a seam and I can't seem to get rid of it! I'm assuming it's got to do with normals and the fact that the difference in angle between the conjoined edges is technically 180. Can anyone help me remove this seam?
Here's my code:
public class MobiusStrip extends Applet {
public static void main(String[] args){
new MainFrame(new MobiusStrip(), 800, 600);
}
#Override
public void init(){
GraphicsConfiguration gc = SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas = new Canvas3D(gc);
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
SimpleUniverse su = new SimpleUniverse(canvas);
su.getViewingPlatform().setNominalViewingTransform();
BranchGroup bg = createSceneGraph();
bg.compile();
su.addBranchGraph(bg);
}
private BranchGroup createSceneGraph(){
BranchGroup root = new BranchGroup();
Shape3D shape = new Shape3D();
shape.setGeometry(mobius().getIndexedGeometryArray());
//Scaling transform
Transform3D tr = new Transform3D();
tr.setScale(0.5);
//Spin transform group
TransformGroup spin = new TransformGroup();
spin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
root.addChild(spin);
//Set appearance
Appearance ap = new Appearance();
PointAttributes pa = new PointAttributes(10, true);
ap.setPointAttributes(pa);
ap.setPolygonAttributes(new PolygonAttributes
(PolygonAttributes.POLYGON_FILL,
PolygonAttributes.CULL_NONE, 0));
//Set materials
Material mat = new Material();
mat.setLightingEnable(true);
mat.setShininess(30);
ap.setMaterial(mat);
//Overarching Transform group
TransformGroup tg = new TransformGroup(tr);
tg.addChild(shape);
spin.addChild(tg);
shape.setAppearance(ap);
//Set rotation
Alpha alpha = new Alpha(-1, 6000);
RotationInterpolator rotate = new RotationInterpolator(alpha, spin);
BoundingSphere bounds = new BoundingSphere();
rotate.setSchedulingBounds(bounds);
spin.addChild(rotate);
//Set background
Background background = new Background(1.0f, 1.0f, 1.0f);
background.setApplicationBounds(bounds);
root.addChild(background);
//Set lighting
AmbientLight light = new AmbientLight(true, new Color3f(Color.BLACK));
light.setInfluencingBounds(bounds);
root.addChild(light);
PointLight ptlight = new PointLight(new Color3f(Color.white),
new Point3f(0.5f,0.5f,1f),
new Point3f(1f,0.2f,0f));
ptlight.setInfluencingBounds(bounds);
root.addChild(ptlight);
return root;
}//Close branchgroup method
//Create the Mobius shape
private GeometryInfo mobius()
{
int m = 100; //number of row points
int n = 100; //number of col points
int p = 4*((m-1)*(n-1)); //faces * points per face
IndexedQuadArray iqa = new IndexedQuadArray(m*n,
GeometryArray.COORDINATES, p);
Point3d[] vertices = new Point3d[m*n];
int index = 0;
//Create vertices
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
double u = i * (2*(Math.PI))/(m - 1);
double v = -0.3 + (j * (0.6/(n-1)));
double x=(1+v*Math.cos(u/2))*Math.cos(u);
double y=(1+v*Math.cos(u/2))*Math.sin(u);
double z=v*Math.sin(u/2);
vertices[index]=new Point3d(x,y,z);
index++;
}//close nested for loop
}//close for loop
iqa.setCoordinates(0, vertices);
index = 0;
//set index for coordinates
for(int i = 0; i < m-1; i++){
for(int j = 0; j < n-1; j++){
iqa.setCoordinateIndex(index, i*m+j);
index++;
iqa.setCoordinateIndex(index, i*m+j+1);
index++;
iqa.setCoordinateIndex(index, (i+1)*m+j+1);
index++;
iqa.setCoordinateIndex(index, (i+1)*m+j);
index++;
}//close nested for loop
}//close for loop
//create geometry info and generate normals for shape
GeometryInfo gi = new GeometryInfo(iqa);
NormalGenerator ng = new NormalGenerator();
ng.generateNormals(gi);
return gi;
}
}
See this question for more explanation. You'll need two changes:
ap.setPolygonAttributes(new PolygonAttributes(PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_BACK, 0));
double u = i * (4 * (Math.PI)) / (m - 1);