using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using FastReport.Export.TTF; using FastReport.Table; using FastReport.Utils; using System; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace FastReport.Export.Pdf { /// /// PDF export (Adobe Acrobat) /// public partial class PDFExport : ExportBase { #region Private Constants const float PDF_DIVIDER = 0.75f; const float PDF_PAGE_DIVIDER = 2.8346400000000003f; // mm to point const int PDF_PRINTOPT = 3; #endregion /// /// Embedded File /// public class EmbeddedFile { private string name; private string description; private DateTime modDate; private EmbeddedRelation relation; private string mime; private Stream fileStream; private long xref; private ZUGFeRD_ConformanceLevel zUGFeRD_ConformanceLevel; /// /// Name of embedded file. /// public string Name { get { return name; } set { name = value; } } /// /// Description of embedded file. /// public string Description { get { return description; } set { description = value; } } /// /// Modify Date of embedded file. /// public DateTime ModDate { get { return modDate; } set { modDate = value; } } /// /// Relationship between the embedded document and the PDF part. /// public EmbeddedRelation Relation { get { return relation; } set { relation = value; } } /// /// Valid MIME type. /// public string MIME { get { return mime; } set { mime = value; } } /// /// Stream of embedded file. /// public Stream FileStream { get { return fileStream; } set { fileStream = value; } } /// /// File reference. /// public long Xref { get { return xref; } set { xref = value; } } /// /// ZUGFeRD Conformance Level. /// public ZUGFeRD_ConformanceLevel ZUGFeRDConformanceLevel { get { return zUGFeRD_ConformanceLevel; } set { zUGFeRD_ConformanceLevel = value; } } /// Initializes a new instance of the class. public EmbeddedFile() { modDate = SystemFake.DateTime.Now; relation = EmbeddedRelation.Alternative; zUGFeRD_ConformanceLevel = ZUGFeRD_ConformanceLevel.BASIC; mime = "text/xml"; fileStream = null; } } #region Public Enums /// /// Default preview size. /// public enum MagnificationFactor { /// /// Actual size /// ActualSize = 0, /// /// Fit Page /// FitPage = 1, /// /// Fit Width /// FitWidth = 2, /// /// Default /// Default = 3, /// /// 10% /// Percent_10 = 4, /// /// 25% /// Percent_25 = 5, /// /// 50% /// Percent_50 = 6, /// /// 75% /// Percent_75 = 7, /// /// 100% /// Percent_100 = 8, /// /// 125% /// Percent_125 = 9, /// /// 150% /// Percent_150 = 10, /// /// 200% /// Percent_200 = 11, /// /// 400% /// Percent_400 = 12, /// /// 800% /// Percent_800 = 13, } /// /// Standard of PDF format. /// public enum PdfStandard { /// /// PDF 1.5 /// None = 0, /// /// PDF/A-1a /// PdfA_1a = 1, /// /// PDF/A-2a /// PdfA_2a = 2, /// /// PDF/A-2b /// PdfA_2b = 3, /// /// PDF/A-2u /// PdfA_2u = 4, /// /// PDF/A-3a /// PdfA_3a = 5, /// /// PDF/A-3b /// PdfA_3b = 6, /// /// Pdf/X-3 /// PdfX_3 = 7, /// /// Pdf/X-4 /// PdfX_4 = 8 } /// /// Color Space. /// public enum PdfColorSpace { /// /// RGB color space /// RGB = 0, /// /// CMYK color space /// CMYK = 1, } /// /// Types of pdf export. /// public enum ExportType { /// /// Simple export /// Export, /// /// Web print mode /// WebPrint } /// /// Relationship between the embedded document and the PDF part. /// public enum EmbeddedRelation { /// /// The embedded file contains data which is used for the visual representation. /// Data, /// /// The embedded file contains the source data for the visual representation derived therefrom in the PDF part. /// Source, /// /// This data relationship should be used if the embedded data are an alternative representation of the PDF contents. /// Alternative, /// /// This data relationship is used if the embedded file serves neither as the source nor as the alternative representation, but the file contains additional information. /// Supplement, /// /// If none of the data relationships above apply or there is an unknown data relationship, this data relationship is used. /// Unspecified } /// /// ZUGFeRD Conformance Level. /// public enum ZUGFeRD_ConformanceLevel { /// /// Basic level. /// BASIC, /// /// Comfort level. /// COMFORT, /// /// Extended level. /// EXTENDED } #endregion #region Private Fields // Options private PdfStandard pdfCompliance = PdfStandard.None; private bool embeddingFonts = true; private bool background = true; private bool textInCurves = false; private PdfColorSpace colorSpace = PdfColorSpace.RGB; private bool imagesOriginalResolution = false; private bool printOptimized = true; private bool jpegCompression = false; private int jpegQuality = 95; private bool interactiveForms = false; private string interactiveFormsFontSetPattern = string.Empty; // end // Document Information private string title = ""; private string author = ""; private string subject = ""; private string keywords = ""; private string creator = "FastReport"; private string producer = "FastReport.NET"; // end // Security private string ownerPassword = ""; private string userPassword = ""; private bool allowPrint = true; private bool allowModify = true; private bool allowCopy = true; private bool allowAnnotate = true; // end // Viewer private bool showPrintDialog = false; private bool hideToolbar = false; private bool hideMenubar = false; private bool hideWindowUI = false; private bool fitWindow = false; private bool centerWindow = true; private bool printScaling = false; private bool outline = true; private MagnificationFactor defaultZoom = MagnificationFactor.ActualSize; // end // Other options private bool displayDocTitle = true; private bool encrypted = false; private bool compressed = true; private bool transparentImages = true; private int richTextQuality = 95; private int defaultPage = 1; private float dpiFX = 96f / DrawUtils.ScreenDpi; private byte[] colorProfile = null; private List embeddedFiles; private bool useFileStream; // end // Internal fields private string fileID; private long rootNumber; private long pagesNumber; private long outlineNumber; private long infoNumber; private long startXRef; private long actionDict; private long printDict; private List xRef; private List pagesRef; private List trasparentStroke; private List trasparentFill; private List pagesHeights; private List pagesTopMargins; private float marginLeft; private float marginWoBottom; private Stream pdf; private float paperWidth; private float paperHeight; private long metaFileId; private long structId; private long colorProfileId; private long attachmentsNamesId; private long attachmentsListId; private IGraphics graphics; private StringBuilder contentBuilder; private long contentsPos; private ExportType exportMode; private string zUGFeRDDescription; private List acroFormsRefs; //private List FAcroFormsAnnotsRefs; private List acroFormsFonts; private string tempFileName; // end // for signature purposes private bool isDigitalSignEnable; private bool isDigitalSignatureInvisible; private DateTime digitalSignCreationDate; private SignatureDictIndicies signatureDictIndicies; private long[] digitalSignByteRange; private System.Security.Cryptography.X509Certificates.X509Certificate2 digitalSignCertificate; private string digitalSignLocation; private string digitalSignReason; private string digitalSignContactInfo; private bool haveToSaveDigitalSignCertificate; private string digitalSignCertificatePath; private string digitalSignCertificatePassword; //end #endregion #region Public Properties #region Options /// /// Gets or sets PDF Compliance standard. /// After set, do not change other settings, it may lead to fail compliance test. /// public PdfStandard PdfCompliance { get { return pdfCompliance; } set { pdfCompliance = value; switch (pdfCompliance) { case PdfStandard.None: break; case PdfStandard.PdfA_1a: case PdfStandard.PdfA_2a: case PdfStandard.PdfA_2b: case PdfStandard.PdfA_2u: case PdfStandard.PdfA_3a: case PdfStandard.PdfA_3b: EmbeddingFonts = true; TextInCurves = false; OwnerPassword = ""; UserPassword = ""; encrypted = false; break; case PdfStandard.PdfX_3: case PdfStandard.PdfX_4: OwnerPassword = ""; UserPassword = ""; encrypted = false; break; } } } /// /// Enable or disable of embedding the TrueType fonts. /// public bool EmbeddingFonts { get { return embeddingFonts; } set { embeddingFonts = value; if (embeddingFonts) textInCurves = false; } } /// /// Enable or disable of exporting the background. /// public bool Background { get { return background; } set { background = value; } } /// /// Enable or disable export text in curves /// public bool TextInCurves { get { return textInCurves; } set { textInCurves = value; if (textInCurves) embeddingFonts = false; } } /// /// Gets or sets PDF color space /// public PdfColorSpace ColorSpace { get { return colorSpace; } set { colorSpace = value; } } /// /// Enables or disables saving images in their original resolution /// public bool ImagesOriginalResolution { get { return imagesOriginalResolution; } set { imagesOriginalResolution = value; } } /// /// Enables or disables optimization of images for printing /// public bool PrintOptimized { get { return printOptimized; } set { printOptimized = value; } } /// /// Enable or disable image jpeg compression /// public bool JpegCompression { get { return jpegCompression; } set { jpegCompression = value; } } /// /// Sets the quality of images in the PDF /// public int JpegQuality { get { return jpegQuality; } set { jpegQuality = value; RichTextQuality = value; } } #endregion #region Document Information /// /// Title of the document. /// public string Title { get { return title; } set { title = value; } } /// /// Author of the document. /// public string Author { get { return author; } set { author = value; } } /// /// Subject of the document. /// public string Subject { get { return subject; } set { subject = value; } } /// /// Keywords of the document. /// public string Keywords { get { return keywords; } set { keywords = value; } } /// /// Creator of the document. /// public string Creator { get { return creator; } set { creator = value; } } /// /// Producer of the document. /// public string Producer { get { return producer; } set { producer = value; } } #endregion #region Security /// /// Sets the owner password. /// public string OwnerPassword { get { return ownerPassword; } set { ownerPassword = value; } } /// /// Sets the user password. /// public string UserPassword { get { return userPassword; } set { userPassword = value; } } /// /// Enable or disable printing in protected document. /// public bool AllowPrint { get { return allowPrint; } set { allowPrint = value; } } /// /// Enable or disable modifying in protected document. /// public bool AllowModify { get { return allowModify; } set { allowModify = value; } } /// /// Enable or disable copying in protected document. /// public bool AllowCopy { get { return allowCopy; } set { allowCopy = value; } } /// /// Enable or disable annotating in protected document. /// public bool AllowAnnotate { get { return allowAnnotate; } set { allowAnnotate = value; } } #endregion #region Viewer /// /// Enable or disable the print dialog window after opening /// public bool ShowPrintDialog { get { return showPrintDialog; } set { showPrintDialog = value; } } /// /// Enable or disable hide the toolbar. /// public bool HideToolbar { get { return hideToolbar; } set { hideToolbar = value; } } /// /// Enable or disable hide the menu's bar. /// public bool HideMenubar { get { return hideMenubar; } set { hideMenubar = value; } } /// /// Enable or disable hide the Windows UI. /// public bool HideWindowUI { get { return hideWindowUI; } set { hideWindowUI = value; } } /// /// Enable or disable of fitting the window /// public bool FitWindow { get { return fitWindow; } set { fitWindow = value; } } /// /// Enable or disable of centering the window. /// public bool CenterWindow { get { return centerWindow; } set { centerWindow = value; } } /// /// Enable or disable of scaling the page for shrink to printable area. /// public bool PrintScaling { get { return printScaling; } set { printScaling = value; } } /// /// Enable or disable of document's Outline. /// public bool Outline { get { return outline; } set { outline = value; } } /// /// Set default zoom on open document /// public MagnificationFactor DefaultZoom { get { return defaultZoom; } set { defaultZoom = value; } } #endregion #region Other /// /// Sets the quality of RichText objects in the PDF /// public int RichTextQuality { get { return richTextQuality; } set { richTextQuality = value; } } /// /// Enable or disable the compression in PDF document. /// public bool Compressed { get { return compressed; } set { compressed = value; } } /// /// Enable or disable of images transparency. /// public bool TransparentImages { get { return transparentImages; } set { transparentImages = value; } } /// /// Enable or disable of displaying document's title. /// public bool DisplayDocTitle { get { return displayDocTitle; } set { displayDocTitle = value; } } /// /// Set default page on open document /// public int DefaultPage { get { return defaultPage; } set { defaultPage = value; } } /// /// Color Profile (ICC file). /// If "null" then default profile will be used /// public byte[] ColorProfile { get { return colorProfile; } set { colorProfile = value; } } /// /// Gets or sets pdf export mode /// public ExportType ExportMode { get { return exportMode; } set { exportMode = value; } } /// /// Gets pdf AcroForms compatibility, if set then EmbeddingFonts = false and PdfCompliance = PdfStandard.None /// public bool InteractiveForms { get { return interactiveForms && PdfCompliance == PdfStandard.None; } set { if (value) PdfCompliance = PdfStandard.None; interactiveForms = value; } } /// /// Set pattern for selection of embedding glyphs for Interactive Forms /// public string InteractiveFormsFontSetPattern { get { return interactiveFormsFontSetPattern; } set { interactiveFormsFontSetPattern = value; } } /// /// Enable or disable using FileStream instead of MemoryStream when exporting. /// Its useful when exporting huge reports on machines with small amount of RAM. /// public bool UseFileStream { get { return useFileStream; } set { useFileStream = value; } } #endregion #region DigitalSign /// /// Enable or disable digital sign for pdf document /// /// /// Be sure to specify a valid certificate for signing using the DigitalSignCertificate property. /// Or using the DigitalSignCertificatePath and DigitalSignCertificatePassword properties. /// public bool IsDigitalSignEnable { get { return isDigitalSignEnable; } set { isDigitalSignEnable = value; } } /// /// Should save and serialize password for digital sign certificate. /// Do not save password unless absolutely necessary!!! /// public bool SaveDigitalSignCertificatePassword { get { return haveToSaveDigitalSignCertificate; } set { haveToSaveDigitalSignCertificate = value; } } /// /// Manualy sets digital sign certificate for exported documents. /// /// /// This property is in priority, i.e. if a certificate is specified, /// the DigitalSignCertificatePath and DigitalSignCertificatePassword properties will not be used. /// public System.Security.Cryptography.X509Certificates.X509Certificate2 DigitalSignCertificate { set { digitalSignCertificate = value; } } /// /// The path for load digital sign certificate. /// public string DigitalSignCertificatePath { get { return digitalSignCertificatePath; } set { digitalSignCertificatePath = value; } } /// /// Sets digital sign certificate password. /// public string DigitalSignCertificatePassword { get { return Crypter.DecryptString(digitalSignCertificatePassword); } set { if (Crypter.IsStringEncrypted(value)) digitalSignCertificatePassword = value; else digitalSignCertificatePassword = Crypter.EncryptString(value); } } /// /// Gets or sets the cpu host name or physical location of the signing /// public string DigitalSignLocation { get { return digitalSignLocation; } set { digitalSignLocation = value; } } /// /// The reason for the signing, such as (I agree ...) /// public string DigitalSignReason { get { return digitalSignReason; } set { digitalSignReason = value; } } /// /// The information to enable the recipient to contact the signer to verify the signature /// public string DigitalSignContactInfo { get { return digitalSignContactInfo; } set { digitalSignContactInfo = value; } } #endregion #endregion #region Private Methods private string getPdfVersion() { switch (PdfCompliance) { case PdfStandard.PdfA_2a: case PdfStandard.PdfA_2b: case PdfStandard.PdfA_2u: case PdfStandard.PdfA_3a: case PdfStandard.PdfA_3b: return "1.7"; case PdfStandard.PdfA_1a: case PdfStandard.PdfX_3: // PDF/X-3:2003 return "1.4"; case PdfStandard.PdfX_4: return "1.6"; case PdfStandard.None: default: return "1.5"; } } private bool isPdfX() { switch (PdfCompliance) { case PdfStandard.PdfX_3: case PdfStandard.PdfX_4: return true; default: return false; } } private bool isPdfA() { switch (PdfCompliance) { case PdfStandard.PdfA_1a: case PdfStandard.PdfA_2a: case PdfStandard.PdfA_2b: case PdfStandard.PdfA_2u: case PdfStandard.PdfA_3a: case PdfStandard.PdfA_3b: return true; default: return false; } } private void AddPDFHeader() { WriteLn(pdf, "%PDF-" + getPdfVersion()); byte[] signature = { 0x25, 0xE2, 0xE3, 0xCF, 0xD3, 0x0D, 0x0A }; pdf.Write(signature, 0, signature.Length); // reserve object for pages UpdateXRef(); } //private void AddPage(ReportPage page) //{ // pageFonts = new List(); // trasparentStroke = new List(); // trasparentFill = new List(); // picResList = new List(); // paperWidth = ExportUtils.GetPageWidth(page) * Units.Millimeters; // paperHeight = ExportUtils.GetPageHeight(page) * Units.Millimeters; // marginWoBottom = (ExportUtils.GetPageHeight(page) - page.TopMargin) * PDF_PAGE_DIVIDER; // marginLeft = page.LeftMargin * PDF_PAGE_DIVIDER; // pagesHeights.Add(marginWoBottom); // pagesTopMargins.Add(page.TopMargin * PDF_PAGE_DIVIDER); // long FContentsPos = 0; // StringBuilder contentBuilder = new StringBuilder(65535); // // page fill // if (background) // using (TextObject pageFill = new TextObject()) // { // pageFill.Fill = page.Fill; // pageFill.Left = -marginLeft / PDF_DIVIDER; // pageFill.Top = -page.TopMargin * PDF_PAGE_DIVIDER / PDF_DIVIDER; // pageFill.Width = ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; // pageFill.Height = ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; // AddTextObject(pageFill, false, contentBuilder); // } // // bitmap watermark on bottom // if (page.Watermark.Enabled && !page.Watermark.ShowImageOnTop) // AddBitmapWatermark(page, contentBuilder); // // text watermark on bottom // if (page.Watermark.Enabled && !page.Watermark.ShowTextOnTop) // AddTextWatermark(page, contentBuilder); // // page borders // if (page.Border.Lines != BorderLines.None) // { // using (TextObject pageBorder = new TextObject()) // { // pageBorder.Border = page.Border; // pageBorder.Left = 0; // pageBorder.Top = 0; // pageBorder.Width = (ExportUtils.GetPageWidth(page) - page.LeftMargin - page.RightMargin) * PDF_PAGE_DIVIDER / PDF_DIVIDER; // pageBorder.Height = (ExportUtils.GetPageHeight(page) - page.TopMargin - page.BottomMargin) * PDF_PAGE_DIVIDER / PDF_DIVIDER; // AddTextObject(pageBorder, true, contentBuilder); // } // } // foreach (Base c in page.AllObjects) // { // ExportObj(c); // } // // bitmap watermark on top // if (page.Watermark.Enabled && page.Watermark.ShowImageOnTop) // AddBitmapWatermark(page, contentBuilder); // // text watermark on top // if (page.Watermark.Enabled && page.Watermark.ShowTextOnTop) // AddTextWatermark(page, contentBuilder); // // write page // FContentsPos = UpdateXRef(); // WriteLn(pdf, ObjNumber(FContentsPos)); // using (MemoryStream tempContentStream = new MemoryStream()) // { // Write(tempContentStream, contentBuilder.ToString()); // tempContentStream.Position = 0; // WritePDFStream(pdf, tempContentStream, FContentsPos, compressed, encrypted, true, true); // } // if (!textInCurves) // if (pageFonts.Count > 0) // for (int i = 0; i < pageFonts.Count; i++) // if (!pageFonts[i].Saved) // { // pageFonts[i].Reference = UpdateXRef(); // pageFonts[i].Saved = true; // } // long PageNumber = UpdateXRef(); // pagesRef.Add(PageNumber); // WriteLn(pdf, ObjNumber(PageNumber)); // StringBuilder sb = new StringBuilder(512); // sb.AppendLine("<<").AppendLine("/Type /Page"); // sb.Append("/MediaBox [0 0 ").Append(FloatToString(ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER)).Append(" "); // sb.Append(FloatToString(ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER)).AppendLine(" ]"); // //margins // if (isPdfX()) // sb.Append("/TrimBox [") // .Append(FloatToString(page.LeftMargin * PDF_PAGE_DIVIDER)).Append(" ") // .Append(FloatToString(page.TopMargin * PDF_PAGE_DIVIDER)).Append(" ") // .Append(FloatToString(page.RightMargin * PDF_PAGE_DIVIDER)).Append(" ") // .Append(FloatToString(page.BottomMargin * PDF_PAGE_DIVIDER)).Append("]"); // sb.AppendLine("/Parent 1 0 R"); // if (!isPdfX()) // { // if (ColorSpace == PdfColorSpace.RGB) // sb.AppendLine("/Group << /Type /Group /S /Transparency /CS /DeviceRGB >>"); // else if (ColorSpace == PdfColorSpace.CMYK) // sb.AppendLine("/Group << /Type /Group /S /Transparency /CS /DeviceCMYK >>"); // } // sb.AppendLine("/Resources << "); // if (pageFonts.Count > 0) // { // sb.Append("/Font << "); // foreach (ExportTTFFont font in pageFonts) // sb.Append(font.Name).Append(" ").Append(ObjNumberRef(font.Reference)).Append(" "); // sb.AppendLine(" >>"); // } // if (isPdfX()) // { // sb.AppendLine("/ExtGState <<"); // for (int i = 0; i < trasparentStroke.Count; i++) // sb.Append("/GS").Append(i.ToString()).Append("S << /Type /ExtGState /ca ").Append(1).AppendLine(" >>"); // for (int i = 0; i < trasparentFill.Count; i++) // sb.Append("/GS").Append(i.ToString()).Append("F << /Type /ExtGState /CA ").Append(1).AppendLine(" >>"); // sb.AppendLine(">>"); // } // else // { // sb.AppendLine("/ExtGState <<"); // for (int i = 0; i < trasparentStroke.Count; i++) // sb.Append("/GS").Append(i.ToString()).Append("S << /Type /ExtGState /ca ").Append(trasparentStroke[i]).AppendLine(" >>"); // for (int i = 0; i < trasparentFill.Count; i++) // sb.Append("/GS").Append(i.ToString()).Append("F << /Type /ExtGState /CA ").Append(trasparentFill[i]).AppendLine(" >>"); // sb.AppendLine(">>"); // } // if (picResList.Count > 0) // { // sb.Append("/XObject << "); // foreach (long resIndex in picResList) // sb.Append("/Im").Append(resIndex.ToString()).Append(" ").Append(ObjNumberRef(resIndex)).Append(" "); // sb.AppendLine(" >>"); // } // sb.AppendLine("/ProcSet [/PDF /Text /ImageC ]"); // sb.AppendLine(">>"); // sb.Append("/Contents ").AppendLine(ObjNumberRef(FContentsPos)); // if (pageAnnots.Length > 0) // { // sb.AppendLine(GetPageAnnots()); // pageAnnots.Length = 0; // } // sb.AppendLine(">>"); // sb.AppendLine("endobj"); // Write(pdf, sb.ToString()); //} private void AddBitmapWatermark(ReportPage page, StringBuilder sb) { if (page.Watermark.Image != null) { using (PictureObject pictureWatermark = new PictureObject()) { pictureWatermark.Left = -marginLeft / PDF_DIVIDER; pictureWatermark.Top = -page.TopMargin * PDF_PAGE_DIVIDER / PDF_DIVIDER; pictureWatermark.Width = ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; pictureWatermark.Height = ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; PictureWatermarkSizeMode(pictureWatermark, page.Watermark.ImageSize); pictureWatermark.Image = new Bitmap(page.Watermark.Image); float transparency = page.Watermark.ImageTransparency; page.Watermark.ImageTransparency = 0; using (Graphics g = Graphics.FromImage(page.Watermark.Image)) { g.Clear(Color.Transparent); page.Watermark.DrawImage(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache), new RectangleF(0, 0, pictureWatermark.Width, pictureWatermark.Height), Report, true); } pictureWatermark.Transparency = page.Watermark.ImageTransparency = transparency; pictureWatermark.Fill = new SolidFill(Color.Transparent); pictureWatermark.FillColor = Color.Transparent; AddPictureObject(pictureWatermark, false, jpegQuality, sb); } } } private void PictureWatermarkSizeMode(PictureObject pictureBoxSize, WatermarkImageSize imageSize) { switch (imageSize) { case WatermarkImageSize.Normal: pictureBoxSize.SizeMode = PictureBoxSizeMode.Normal; break; case WatermarkImageSize.Center: pictureBoxSize.SizeMode = PictureBoxSizeMode.CenterImage; break; case WatermarkImageSize.Stretch: pictureBoxSize.SizeMode = PictureBoxSizeMode.StretchImage; break; case WatermarkImageSize.Zoom: pictureBoxSize.SizeMode = PictureBoxSizeMode.Zoom; break; case WatermarkImageSize.Tile: pictureBoxSize.Tile = imageSize == WatermarkImageSize.Tile; break; default: pictureBoxSize.SizeMode = PictureBoxSizeMode.AutoSize; break; } } private void AddTextWatermark(ReportPage page, StringBuilder sb) { if (!String.IsNullOrEmpty(page.Watermark.Text)) using (TextObject textWatermark = new TextObject()) { textWatermark.HorzAlign = HorzAlign.Center; textWatermark.VertAlign = VertAlign.Center; textWatermark.Left = -marginLeft / PDF_DIVIDER; textWatermark.Top = -page.TopMargin * PDF_PAGE_DIVIDER / PDF_DIVIDER; textWatermark.Width = ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; textWatermark.Height = ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; textWatermark.Text = page.Watermark.Text; textWatermark.TextFill = page.Watermark.TextFill; if (page.Watermark.TextRotation == WatermarkTextRotation.Vertical) textWatermark.Angle = 270; else if (page.Watermark.TextRotation == WatermarkTextRotation.ForwardDiagonal) textWatermark.Angle = 360 - (int)(Math.Atan(textWatermark.Height / textWatermark.Width) * (180 / Math.PI)); else if (page.Watermark.TextRotation == WatermarkTextRotation.BackwardDiagonal) textWatermark.Angle = (int)(Math.Atan(textWatermark.Height / textWatermark.Width) * (180 / Math.PI)); textWatermark.Font = page.Watermark.Font; if (page.Watermark.TextFill is SolidFill) textWatermark.TextColor = (page.Watermark.TextFill as SolidFill).Color; textWatermark.Fill = new SolidFill(Color.Transparent); textWatermark.FillColor = Color.Transparent; AddTextObject(textWatermark, false, sb); } } private void AddTable(TableBase table, bool drawCells, StringBuilder sb_in) { float y = 0; StringBuilder sb = new StringBuilder(1024); for (int i = 0; i < table.RowCount; i++) { float x = 0; for (int j = 0; j < table.ColumnCount; j++) { if (!table.IsInsideSpan(table[j, i])) { table[j, i].Left = x; table[j, i].Top = y; if (drawCells) { Border oldBorder = table[j, i].Border.Clone(); table[j, i].Border.Lines = BorderLines.None; if (table[j, i] is TextObject) AddTextObject(table[j, i] as TextObject, false, sb_in); else AddPictureObject(table[j, i] as ReportComponentBase, false, jpegQuality, sb_in); table[j, i].Border = null; table[j, i].Border = oldBorder; } else DrawPDFBorder(table[j, i].Border, table[j, i].AbsLeft, table[j, i].AbsTop, table[j, i].Width, table[j, i].Height, sb); } x += (table.Columns[j]).Width; } y += (table.Rows[i]).Height; } sb_in.Append(sb); } private void AddShape(ShapeObject shapeObject, StringBuilder sb) { // Shapes with a custom dash pattern are exported as images. // There is a difference between shapes with a custom dash pattern and shapes with standard Border styles in the display in pdf - // shapes with standard Border styles in pdf are larger than in the preview. if (shapeObject.Shape != ShapeKind.RoundRectangle && shapeObject.Fill is SolidFill && shapeObject.DashPattern.Count == 0) { if (shapeObject.Shape == ShapeKind.Rectangle) { DrawPDFFillRect( GetLeft(shapeObject.AbsLeft), GetTop(shapeObject.AbsTop), shapeObject.Width * PDF_DIVIDER, shapeObject.Height * PDF_DIVIDER, shapeObject.Fill, sb); DrawPDFRect( GetLeft(shapeObject.AbsLeft), GetTop(shapeObject.AbsTop), GetLeft(shapeObject.AbsLeft + shapeObject.Width), GetTop(shapeObject.AbsTop + shapeObject.Height), shapeObject.Border.Color, shapeObject.Border.Width * PDF_DIVIDER, shapeObject.Border.Style, sb); } else if (shapeObject.Shape == ShapeKind.Triangle) DrawPDFTriangle(GetLeft(shapeObject.AbsLeft), GetTop(shapeObject.AbsTop), shapeObject.Width * PDF_DIVIDER, shapeObject.Height * PDF_DIVIDER, shapeObject.FillColor, shapeObject.Border.Color, shapeObject.Border.Width * PDF_DIVIDER, shapeObject.Border.Style, sb); else if (shapeObject.Shape == ShapeKind.Diamond) DrawPDFDiamond(GetLeft(shapeObject.AbsLeft), GetTop(shapeObject.AbsTop), shapeObject.Width * PDF_DIVIDER, shapeObject.Height * PDF_DIVIDER, shapeObject.FillColor, shapeObject.Border.Color, shapeObject.Border.Width * PDF_DIVIDER, shapeObject.Border.Style, sb); else if (shapeObject.Shape == ShapeKind.Ellipse) DrawPDFEllipse(GetLeft(shapeObject.AbsLeft), GetTop(shapeObject.AbsTop), shapeObject.Width * PDF_DIVIDER, shapeObject.Height * PDF_DIVIDER, shapeObject.FillColor, shapeObject.Border.Color, shapeObject.Border.Width * PDF_DIVIDER, shapeObject.Border.Style, sb); if (!isPdfX()) AddAnnot(shapeObject); } else AddPictureObject(shapeObject, true, jpegQuality, sb); } //private void AddPolyLine(PolyLineObject obj, StringBuilder sb) //{ // int len = obj.PointsArray.Length; // if (len == 0 || len == 1) // { // float localX = GetLeft(obj.AbsLeft); // float localY = GetTop(obj.AbsTop); // DrawPDFLine( // localX, localY + 6 * PDF_DIVIDER, // localX, localY - 6 * PDF_DIVIDER, // obj.Border.Color, obj.Border.Width * PDF_DIVIDER, obj.Border.Style, null, null, sb); // DrawPDFLine( // localX - 6 * PDF_DIVIDER, localY, // localX + 6 * PDF_DIVIDER, localY, // obj.Border.Color, obj.Border.Width * PDF_DIVIDER, obj.Border.Style, null, null, sb); // } // else if (len == 2) // { // DrawPDFLine( // GetLeft(obj.AbsLeft) + (obj.PointsArray[0].X + obj.CenterX) * PDF_DIVIDER, // GetTop(obj.AbsTop) - (obj.PointsArray[0].Y + obj.CenterY) * PDF_DIVIDER, // GetLeft(obj.AbsLeft) + (obj.PointsArray[1].X + obj.CenterX) * PDF_DIVIDER, // GetTop(obj.AbsTop) - (obj.PointsArray[1].Y + obj.CenterY) * PDF_DIVIDER, obj.Border.Color, obj.Border.Width * PDF_DIVIDER, obj.Border.Style, null, null, sb); // } // else // { // if (obj is PolygonObject) // { // if (obj.Fill is SolidFill) // DrawPDFPolygon(GetLeft(obj.AbsLeft), // GetTop(obj.AbsTop), GetLeft(obj.AbsLeft + obj.Width), GetTop(obj.AbsTop + obj.Height), // obj.CenterX, obj.CenterY, obj.PointsArray, obj.FillColor, // obj.Border.Color, obj.Border.Width * PDF_DIVIDER, obj.Border.Style, sb); // //else if (obj.Fill is LinearGradientFill || obj.Fill is PathGradientFill) // // FillPDFGraphicsPath(GetLeft(obj.AbsLeft), // // GetTop(obj.AbsTop), obj.Width * PDF_DIVIDER, obj.Height * PDF_DIVIDER, // // obj.GetPath(null, 0,0,0,0, PDF_DIVIDER, PDF_DIVIDER), // // obj.Fill.CreateBrush(new RectangleF(0, 0, obj.Width, obj.Height)), sb); // else // AddPictureObject(obj, true, jpegQuality, sb); // } // else // DrawPDFPolyLine(GetLeft(obj.AbsLeft), // GetTop(obj.AbsTop), GetLeft(obj.AbsLeft + obj.Width), GetTop(obj.AbsTop + obj.Height), // obj.CenterX, obj.CenterY, obj.PointsArray, false, // obj.Border.Color, obj.Border.Width * PDF_DIVIDER, obj.Border.Style, sb); // } //} private void AddLine(LineObject l, StringBuilder sb) { if (l.DashPattern.Count == 1) l.Border.Style = LineStyle.Solid; DrawPDFLine(GetLeft(l.AbsLeft), GetTop(l.AbsTop), GetLeft(l.AbsLeft + l.Width), GetTop(l.AbsTop + l.Height), l.Border.Color, l.Border.Width * PDF_DIVIDER, l.DashPattern.Count > 1 ? LineStyle.Custom : l.Border.Style, l.StartCap, l.EndCap, sb, l.DashPattern); } private void AddBandObject(BandBase band, StringBuilder sb) { if (band.Border.Lines == BorderLines.None && band.Fill.IsTransparent) return; using (TextObject newObj = new TextObject()) { newObj.Left = band.AbsLeft; newObj.Top = band.AbsTop; newObj.Width = band.Width; newObj.Height = band.Height; newObj.Fill = band.Fill; newObj.Border = band.Border; AddTextObject(newObj, true, sb); } } private void AddTextObject(TextObject obj, bool drawBorder, StringBuilder sb_in) { if (!isPdfX()) AddAnnot(obj); StringBuilder sb = new StringBuilder(256); StringBuilder image_sb = null; sb.AppendLine("q"); sb.Append(FloatToString(GetLeft(obj.AbsLeft))).Append(" "); sb.Append(FloatToString(GetTop(obj.AbsTop + obj.Height))).Append(" "); sb.Append(FloatToString((obj.Width) * PDF_DIVIDER)).Append(" "); sb.Append(FloatToString((obj.Height) * PDF_DIVIDER)).AppendLine(" re"); if (obj.Clip) sb.AppendLine("W"); sb.AppendLine("n"); // draw background if (obj.Fill is SolidFill || (obj.Fill is GlassFill && !(obj.Fill as GlassFill).Hatch) || IsFillableGradientGrid(obj.Fill)) { DrawPDFFillRect(GetLeft(obj.AbsLeft), GetTop(obj.AbsTop), obj.Width * PDF_DIVIDER, obj.Height * PDF_DIVIDER, obj.Fill, sb); } else if (obj.Width > 0 && obj.Height > 0) { using (PictureObject backgroundPicture = new PictureObject()) { backgroundPicture.Left = obj.AbsLeft; backgroundPicture.Top = obj.AbsTop; backgroundPicture.Width = obj.Width; backgroundPicture.Height = obj.Height; backgroundPicture.Image = new Bitmap((int)backgroundPicture.Width, (int)backgroundPicture.Height); using (Graphics g = Graphics.FromImage(backgroundPicture.Image)) { g.Clear(Color.Transparent); g.TranslateTransform(-obj.AbsLeft, -obj.AbsTop); BorderLines oldLines = obj.Border.Lines; obj.Border.Lines = BorderLines.None; string oldText = obj.Text; obj.Text = String.Empty; obj.Draw(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache)); obj.Text = oldText; obj.Border.Lines = oldLines; } AddPictureObject(backgroundPicture, false, jpegQuality, sb_in); } } switch (obj.TextRenderType) { case TextRenderType.HtmlParagraph: { RectangleF textRect = new RectangleF( obj.AbsLeft + obj.Padding.Left, obj.AbsTop + obj.Padding.Top, obj.Width - obj.Padding.Horizontal, obj.Height - obj.Padding.Vertical); AddTextObjectHtmlInternal(obj, textRect, sb); } break; default: if (obj.Underlines) AppendUnderlines(sb, obj); if (obj.Editable && InteractiveForms) { long xref = AddTextDefaultValueForEditable(obj); AddTextField(obj, xref); } else if (!String.IsNullOrEmpty(obj.Text)) { RectangleF textRect = new RectangleF( obj.AbsLeft + obj.Padding.Left, obj.AbsTop + obj.Padding.Top, obj.Width - obj.Padding.Horizontal, obj.Height - obj.Padding.Vertical); bool transformNeeded = obj.Angle != 0 || obj.FontWidthRatio != 1; // transform, rotate and scale pdf coordinates if needed if (transformNeeded) { textRect.X = -textRect.Width / 2; textRect.Y = -textRect.Height / 2; float angle = (float)((360 - obj.Angle) * Math.PI / 180); float sin = (float)Math.Sin(angle); float cos = (float)Math.Cos(angle); float x = GetLeft(obj.AbsLeft + obj.Width / 2); float y = GetTop(obj.AbsTop + obj.Height / 2); // offset the origin to the middle of bounding rectangle, then rotate sb.Append(FloatToString(cos)).Append(" "). Append(FloatToString(sin)).Append(" "). Append(FloatToString(-sin)).Append(" "). Append(FloatToString(cos)).Append(" "). Append(FloatToString(x)).Append(" "). Append(FloatToString(y)).AppendLine(" cm"); // apply additional matrix to scale x coordinate if (obj.FontWidthRatio != 1) sb.Append(FloatToString(obj.FontWidthRatio)).AppendLine(" 0 0 1 0 0 cm"); } image_sb = AddTextObjectInternal(obj, textRect, transformNeeded, sb, false); } break; } sb.AppendLine("Q"); if (drawBorder) DrawPDFBorder(obj.Border, obj.AbsLeft, obj.AbsTop, obj.Width, obj.Height, sb); sb_in.Append(sb); if (image_sb != null) sb_in.Append(image_sb); } private void AddTextObjectHtmlInternal(TextObject obj, RectangleF textRect, StringBuilder sb) { StringFormat format = obj.GetStringFormat(Report.GraphicCache /*cache*/, 0); Color color = Color.Black; if (obj.TextFill is SolidFill) color = (obj.TextFill as SolidFill).Color; using (HtmlTextRenderer renderer = new HtmlTextRenderer(obj.Text, graphics, obj.Font.FontFamily, obj.Font.Size, obj.Font.Style, color, obj.TextOutline.Color, textRect, obj.Underlines, format, obj.HorzAlign, obj.VertAlign, obj.ParagraphFormat.MultipleScale(dpiFX), obj.ForceJustify, dpiFX, dpiFX, obj.InlineImageCache, false, obj.TabPositions.Count > 0)) { foreach (HtmlTextRenderer.RectangleFColor rect in renderer.Backgrounds) DrawPDFFillRect(GetLeft(rect.Left), GetTop(rect.Top), rect.Width * PDF_DIVIDER, rect.Height * PDF_DIVIDER, new SolidFill(rect.Color), sb); List pictures = new List(); if (obj.RightToLeft) { foreach (HtmlTextRenderer.Paragraph paragraph in renderer.Paragraphs) foreach (HtmlTextRenderer.Line line in paragraph.Lines) foreach (HtmlTextRenderer.Word word in line.Words) if (word.Type == HtmlTextRenderer.WordType.Normal) foreach (HtmlTextRenderer.Run run in word.Runs) { if (run is HtmlTextRenderer.RunText) { HtmlTextRenderer.RunText runText = run as HtmlTextRenderer.RunText; using (Font fnt = runText.Style.GetFont()) { AppendText(sb, fnt, run.Left - run.Width + fnt.Size * 0.2f, run.Top, 0, run.Width, runText.Text, obj.RightToLeft, false, obj.TextOutline, run.Style.Color, false, 0); } } else if (run is HtmlTextRenderer.RunImage) { HtmlTextRenderer.RunImage runImage = run as HtmlTextRenderer.RunImage; PictureObject picture = new PictureObject(); picture.Left = (runImage.Left - runImage.Width) / renderer.Scale; picture.Top = runImage.Top / renderer.Scale; picture.Width = runImage.Width / renderer.Scale; picture.Height = runImage.Height / renderer.Scale; picture.SizeMode = PictureBoxSizeMode.StretchImage; picture.Image = runImage.Image; picture.SetReport(obj.Report); pictures.Add(picture); } } } else { foreach (HtmlTextRenderer.Paragraph paragraph in renderer.Paragraphs) foreach (HtmlTextRenderer.Line line in paragraph.Lines) foreach (HtmlTextRenderer.Word word in line.Words) if (word.Type == HtmlTextRenderer.WordType.Normal) foreach (HtmlTextRenderer.Run run in word.Runs) { if (run is HtmlTextRenderer.RunText) { HtmlTextRenderer.RunText runText = run as HtmlTextRenderer.RunText; using (Font fnt = runText.Style.GetFont()) { AppendText(sb, fnt, run.Left, run.Top, 0, run.Width, runText.Text, obj.RightToLeft, false, obj.TextOutline, run.Style.Color, false, 0); } } else if (run is HtmlTextRenderer.RunImage) { HtmlTextRenderer.RunImage runImage = run as HtmlTextRenderer.RunImage; PictureObject picture = new PictureObject(); picture.Left = runImage.Left / renderer.Scale; picture.Top = runImage.Top / renderer.Scale; picture.Width = runImage.Width / renderer.Scale; picture.Height = runImage.Height / renderer.Scale; picture.SizeMode = PictureBoxSizeMode.StretchImage; picture.Image = runImage.Image; picture.SetReport(obj.Report); pictures.Add(picture); } } } foreach (PictureObject pobj in pictures) { try { AddPictureObject(pobj, false, jpegQuality, sb); } finally { pobj.Dispose(); } } foreach (HtmlTextRenderer.LineFColor line in renderer.Underlines) DrawPDFLine(GetLeft(line.Left), GetTop(line.Top), GetLeft(line.Right), GetTop(line.Top), line.Color, line.Width * PDF_DIVIDER, LineStyle.Solid, null, null, sb); foreach (HtmlTextRenderer.LineFColor line in renderer.Stikeouts) DrawPDFLine(GetLeft(line.Left), GetTop(line.Top), GetLeft(line.Right), GetTop(line.Top), line.Color, line.Width * PDF_DIVIDER, LineStyle.Solid, null, null, sb); } } private StringBuilder AddTextObjectInternal(TextObject obj, RectangleF textRect, bool transformNeeded, StringBuilder sb, bool bbox) { StringBuilder image_sb = null; StringFormat format = obj.GetStringFormat(Report.GraphicCache /*cache*/, 0); Brush textBrush = Report.GraphicCache.GetBrush(obj.TextColor); Font f = Report.GraphicCache.GetFont(obj.Font.FontFamily, obj.Font.Size * dpiFX, obj.Font.Style); float w = Config.IsRunningOnMono ? 0 : f.GetHeight(graphics.Graphics) * 0.1f;// to match .net char X offset. #if SKIA w = f.Height * 0.1f; if (obj.HorzAlign == HorzAlign.Right) w *= 2; #else // invert offset in case of rtl if (obj.RightToLeft) w = -w; #endif // we don't need this offset if text is centered if (obj.HorzAlign == HorzAlign.Center) w = 0; if (obj.TextRenderType == TextRenderType.Inline) w = 0; // break the text to paragraphs, lines, words and runs AdvancedTextRenderer renderer = new AdvancedTextRenderer(obj.Text, graphics, f, textBrush, null, textRect, format, obj.HorzAlign, obj.VertAlign, obj.LineHeight, obj.Angle, obj.FontWidthRatio, obj.ForceJustify, obj.Wysiwyg, obj.HasHtmlTags, true, dpiFX, dpiFX, obj.InlineImageCache); if (obj.HasHtmlTags) { image_sb = new StringBuilder(); foreach (PictureObject pobj in obj.GetPictureFromHtmlText(renderer)) { pobj.SetReport(obj.Report); AddPictureObject(pobj, false, jpegQuality, image_sb); } } // render foreach (AdvancedTextRenderer.Paragraph paragraph in renderer.Paragraphs) foreach (AdvancedTextRenderer.Line line in paragraph.Lines) { float lineOffset = 0; float lineHeight = line.CalcHeight(); float objHeight = (obj.Angle == 0 || obj.Angle == 180) ? obj.Height - obj.Padding.Vertical : obj.Width - obj.Padding.Horizontal; if (lineHeight > objHeight) { if (obj.VertAlign == VertAlign.Center) lineOffset = -lineHeight / 2; else if (obj.VertAlign == VertAlign.Bottom) lineOffset = -lineHeight; } float lineThickness = lineHeight * 0.05f; foreach (RectangleF rect in line.Underlines) DrawPDFLine(rect.Left, rect.Top + GetBaseline(f) + 2 + lineThickness, rect.Width, lineThickness, w, obj.TextColor, transformNeeded, sb); foreach (RectangleF rect in line.Strikeouts) DrawPDFLine(rect.Left, rect.Top + lineHeight / 2, rect.Width, lineThickness * 0.7f, w, obj.TextColor, transformNeeded, sb); foreach (AdvancedTextRenderer.Word word in line.Words) if (renderer.HtmlTags) foreach (AdvancedTextRenderer.Run run in word.Runs) using (Font fnt = run.GetFont()) { AppendText(sb, fnt, run.Left, run.Top, w, run.Width, run.Text, obj.RightToLeft, transformNeeded, obj.TextOutline, run.Style.Color, bbox, obj.Height - obj.Padding.Vertical); } else AppendText(sb, f, word.Left, word.Top + lineOffset, w, word.Width, word.Text, obj.RightToLeft, transformNeeded, obj.TextOutline, obj.TextColor, bbox, obj.Height - obj.Padding.Vertical); } return image_sb; } private void AppendUnderlines(StringBuilder Result, TextObject obj) { float lineHeight = obj.LineHeight == 0 ? obj.Font.GetHeight() : obj.LineHeight; lineHeight *= dpiFX * PDF_DIVIDER; float curY = GetTop(obj.AbsTop) - lineHeight; float bottom = GetTop(obj.AbsBottom); float left = GetLeft(obj.AbsLeft); float right = GetLeft(obj.AbsRight); float width = obj.Border.Width * PDF_DIVIDER; while (curY > bottom) { DrawPDFLine(left, curY, right, curY, obj.Border.Color, width, LineStyle.Solid, null, null, Result); curY -= lineHeight; } } private void DrawText(StringBuilder Result, float x, float y, string s, ExportTTFFont font, Color fontColor) { bool SimulateItalic = font.NeedSimulateItalic && font.SourceFont.Italic; bool SimulateBold = font.NeedSimulateBold && font.SourceFont.Bold; Result.AppendLine("BT"); Result.Append(FloatToString(x)).Append(" ").Append(FloatToString(y)).AppendLine(" Td"); if (SimulateItalic) Result.Append("1 0 0.3333 1 ").Append(FloatToString(x)).Append(' ').Append(FloatToString(y)).AppendLine(" Tm"); if (SimulateBold) { GetPDFStrokeColor(fontColor, Result); Result.Append("2 Tr ").Append(FloatToString(font.SourceFontBoldMultiplier)).Append(" w "); } else Result.AppendLine("0 Tr"); Result.Append("[<").Append(s).AppendLine(">] TJ"); if (SimulateBold) Result.AppendLine("0 Tr"); Result.AppendLine("ET"); } private void DrawTextOutline(StringBuilder Result, float x, float y, string s, TextOutline objTextOutline) { Result.AppendLine(); GetPDFStrokeColor(objTextOutline.Color, Result); Result.AppendLine("BT"); Result.Append(FloatToString(x)).Append(" ").Append(FloatToString(y)).AppendLine(" Td"); Result.AppendLine("1 Tr"); Result.Append(FloatToString(objTextOutline.Width * PDF_DIVIDER)).AppendLine(" w"); Result.Append("[<").Append(s).AppendLine(">] TJ"); Result.AppendLine("ET"); } private string GetGlyphString(ExportTTFFont.RunInfo run, float width) { #if WITHOUT_UNISCRIBE && !SKIA // in linux + libgdiplus, kerning is used to display and measure text. Width of text reported by Graphics.MeasureString differs from sum of glyph widths in pdf. // case: right-aligned text such as // 11111111111111111111111111111111 // 1 // may produce pdf output like this: // 11111111111111111111111111111111 // 1 // or this: // 11111111111111111111111111111111 // 1 // depending on font used. // Solution: we have got desired width and actual pdf width. Calculate delta and use it (along with kerning) for inter-character spacing float pdfWidth = 0; int lastNotWhiteSpaceIndex = Array.FindLastIndex(run.GlyphToUnicode, c => !string.IsNullOrWhiteSpace(c)); for (int i = 0; i <= lastNotWhiteSpaceIndex; i++) { pdfWidth += run.Widths[i] + run.Kernings[i]; } float delta = width == 0 ? 0 : (pdfWidth - width * dpiFX * 750 / run.Font.Size) / run.Glyphs.Length; #else // windows gdi+ does not use kerning. Also, MeasureString returns the correct width so no adjustment is needed. // Skia also measures correctly. float delta = 0; #endif // convert glyph indices to a string StringBuilder sb = new StringBuilder(run.Glyphs.Length * 4); for (int i = 0; i < run.Glyphs.Length; i++) { sb.Append(run.Glyphs[i].ToString("X4")); int offs = (int)Math.Round(-run.Kernings[i] + delta); // dpiFX is not needed here. if (offs != 0) sb.Append(">" + offs.ToString() + "<"); } return sb.ToString(); } private void DrawTextRun(ExportTTFFont.RunInfo run, StringBuilder result, float x, float y, float width, ExportTTFFont pdffont, TextOutline objTextOutline, Color fontColor) { string s = GetGlyphString(run, width); if (objTextOutline == null || !objTextOutline.Enabled) { DrawText(result, x, y, s, pdffont, fontColor); } else { if (objTextOutline.DrawBehind) { DrawTextOutline(result, x, y, s, objTextOutline); DrawText(result, x, y, s, pdffont, fontColor); } else { DrawText(result, x, y, s, pdffont, fontColor); DrawTextOutline(result, x, y, s, objTextOutline); } } } private void DrawGlyphRun(ExportTTFFont.RunInfo run, StringBuilder result, float x, float y, ExportTTFFont pdffont, TextOutline objTextOutline, Color fontColor) { ExportTTFFont.GlyphTTF[] txt = pdffont.GetGlyphPath(run); Action DrawText = () => { float shift = 0; foreach (ExportTTFFont.GlyphTTF ch in txt) { DrawPDFPolygonChar(ch.Path, x + shift * PDF_DIVIDER, y, fontColor, result); shift += ch.Width; } }; Action DrawOutline = () => { float shift = 0; foreach (ExportTTFFont.GlyphTTF ch in txt) { DrawPDFPolygonCharOutline(ch.Path, x + shift * PDF_DIVIDER, y, objTextOutline.Color, objTextOutline.Width, result); shift += ch.Width; } }; if (objTextOutline == null || !objTextOutline.Enabled) { DrawText(); } else { if (objTextOutline.DrawBehind) { DrawOutline(); DrawText(); } else { DrawText(); DrawOutline(); } } } private void AppendText(StringBuilder result, Font font, float x, float y, float offsX, float width, string text, bool rtl, bool transformNeeded, TextOutline objTextOutline, Color fontColor, bool bbox, float bbox_height) { // set up initial font int fontNumber = GetObjFontNumber(font); ExportTTFFont pdffont = pageFonts[fontNumber]; Font currentFont = null; // this may return several runs if text is bidi or font fallback is needed ExportTTFFont.RunInfo[] runs = pdffont.GetFontRuns(text, rtl, font); // width correction (for libgdiplus) must be switched off if we got several runs if (runs.Length > 1) width = 0; foreach (var run in runs) { // each run may have its own font. if (run.Font != currentFont) { currentFont = run.Font; fontNumber = GetObjFontNumber(currentFont); AppendFont(result, fontNumber, currentFont.Size / dpiFX, fontColor); pdffont = pageFonts[fontNumber]; } pdffont.AddUsedGlyphs(run); float textX = x + run.OffsetX; // run with vertical offset has single glyph, use its 0 index float textY = y + run.VerticalOffsets[0]; if (!bbox) { textX = (transformNeeded ? textX * PDF_DIVIDER : GetLeft(textX)) + offsX; textY = transformNeeded ? -textY * PDF_DIVIDER : GetTop(textY); textY -= GetBaseline(currentFont) * PDF_DIVIDER; } else { textX = textX * PDF_DIVIDER + offsX; textY = (bbox_height - textY - GetBaseline(currentFont)) * PDF_DIVIDER; } if (textInCurves) DrawGlyphRun(run, result, textX, textY, pdffont, objTextOutline, fontColor); else DrawTextRun(run, result, textX, textY, width, pdffont, objTextOutline, fontColor); } } private string GetZoomString(int page, float zoom) { return ExportUtils.StringFormat(" /XYZ 0 {0} {1}", Math.Round(pagesHeights[page - 1] + pagesTopMargins[page - 1]).ToString(), FloatToString(zoom)); } private void SetMagnificationFactor(int PageNumber, MagnificationFactor factor) { if (factor == MagnificationFactor.Default) return; if (pagesRef.Count <= 0) return; string Magnificator = ""; actionDict = UpdateXRef(); WriteLn(pdf, ObjNumber(actionDict)); WriteLn(pdf, "<<"); WriteLn(pdf, "/S /GoTo"); switch (factor) { case MagnificationFactor.ActualSize: Magnificator = GetZoomString(PageNumber, 1f); break; case MagnificationFactor.FitPage: Magnificator = " /Fit"; break; case MagnificationFactor.FitWidth: Magnificator = " /FitH 0"; break; case MagnificationFactor.Percent_10: Magnificator = GetZoomString(PageNumber, 0.1f); break; case MagnificationFactor.Percent_25: Magnificator = GetZoomString(PageNumber, 0.25f); break; case MagnificationFactor.Percent_50: Magnificator = GetZoomString(PageNumber, 0.5f); break; case MagnificationFactor.Percent_75: Magnificator = GetZoomString(PageNumber, 0.75f); break; case MagnificationFactor.Percent_100: Magnificator = GetZoomString(PageNumber, 1f); break; case MagnificationFactor.Percent_125: Magnificator = GetZoomString(PageNumber, 1.25f); break; case MagnificationFactor.Percent_150: Magnificator = GetZoomString(PageNumber, 1.5f); break; case MagnificationFactor.Percent_200: Magnificator = GetZoomString(PageNumber, 2f); break; case MagnificationFactor.Percent_400: Magnificator = GetZoomString(PageNumber, 4f); break; case MagnificationFactor.Percent_800: Magnificator = GetZoomString(PageNumber, 8f); break; } string targetPage = ObjNumberRef(pagesRef[PageNumber - 1]); WriteLn(pdf, ExportUtils.StringFormat("/D [{0}{1}]", targetPage, Magnificator)); WriteLn(pdf, ">>"); WriteLn(pdf, "endobj"); } private void AddPDFFooter() { if (!textInCurves) { foreach (ExportTTFFont font in fonts) { WriteFont(font); } } pagesNumber = 1; xRef[0] = pdf.Position; WriteLn(pdf, ObjNumber(pagesNumber)); WriteLn(pdf, "<<"); WriteLn(pdf, "/Type /Pages"); Write(pdf, "/Kids ["); foreach (long page in pagesRef) Write(pdf, ObjNumberRef(page) + " "); WriteLn(pdf, "]"); WriteLn(pdf, "/Count " + pagesRef.Count.ToString()); WriteLn(pdf, ">>"); WriteLn(pdf, "endobj"); if (outline) { FastReport.Preview.Outline outlineTree = Report.PreparedPages.Outline; outlineNumber = UpdateXRef(); this.outlineTree = new PDFOutlineNode(); this.outlineTree.number = outlineNumber; BuildOutline(this.outlineTree, outlineTree.Xml); WriteOutline(this.outlineTree); } if (!isPdfX() && defaultZoom != MagnificationFactor.Default && defaultPage < Report.PreparedPages.Count) SetMagnificationFactor(defaultPage, defaultZoom); if (!isPdfX()) WriteAnnots(); if (isPdfX()) { AddMetaDataPdfX(); if (ColorSpace == PdfColorSpace.RGB) AddColorProfile("PDFX", "pdfxprofile"); else if (ColorSpace == PdfColorSpace.CMYK) AddColorProfile("PDFX", "pdfcmykprofile"); } if (isPdfA()) { AddAttachments(); AddStructure(); AddMetaDataPdfA(); if (ColorSpace == PdfColorSpace.RGB) AddColorProfile("PDFA1", "pdfaprofile"); else if (ColorSpace == PdfColorSpace.CMYK) AddColorProfile("PDFA1", "pdfcmykprofile"); } infoNumber = UpdateXRef(); StringBuilder sb = new StringBuilder(1024); sb.AppendLine(ObjNumber(infoNumber)); sb.Append("<<"); sb.Append("/Title "); if (isPdfX() && String.IsNullOrEmpty(title)) title = "FastReport.Net"; PrepareString(title, encKey, encrypted, infoNumber, sb); sb.Append("/Author "); PrepareString(author, encKey, encrypted, infoNumber, sb); sb.Append("/Subject "); PrepareString(subject, encKey, encrypted, infoNumber, sb); sb.Append("/Keywords "); PrepareString(keywords, encKey, encrypted, infoNumber, sb); sb.Append("/Creator "); PrepareString(creator, encKey, encrypted, infoNumber, sb); sb.Append("/Producer "); PrepareString(producer, encKey, encrypted, infoNumber, sb); string s = "D:" + digitalSignCreationDate.ToString("yyyyMMddHHmmssZ"); if (encrypted) { sb.Append("/CreationDate "); PrepareString(s, encKey, encrypted, infoNumber, sb); sb.Append("/ModDate "); PrepareString(s, encKey, encrypted, infoNumber, sb); } else { sb.AppendLine("/CreationDate (" + s + ")"); sb.AppendLine("/ModDate (" + s + ")"); } if (PdfCompliance == PdfStandard.PdfX_3) { sb.AppendLine("/GTS_PDFXVersion(PDF/X-3:2003)"); sb.AppendLine("/Trapped /False"); } else if (PdfCompliance == PdfStandard.PdfX_4) { sb.AppendLine("/GTS_PDFXVersion(PDF/X-4)"); sb.AppendLine("/Trapped /False"); } sb.AppendLine(">>"); sb.AppendLine("endobj"); Write(pdf, sb.ToString()); //acroform long acroform = AddAcroForm(); //end_acroform rootNumber = UpdateXRef(); WriteLn(pdf, ObjNumber(rootNumber)); WriteLn(pdf, "<<"); WriteLn(pdf, "/Type /Catalog"); WriteLn(pdf, "/Version /" + getPdfVersion()); WriteLn(pdf, "/MarkInfo << /Marked true >>"); if (acroform > 0) WriteLn(pdf, "/AcroForm " + ObjNumberRef(acroform).ToString()); WriteLn(pdf, "/Pages " + ObjNumberRef(pagesNumber)); if (!isPdfX()) { if (defaultZoom != MagnificationFactor.Default) WriteLn(pdf, "/OpenAction " + ObjNumberRef(actionDict)); } if (showPrintDialog) { WriteLn(pdf, "/Names <>"); } Write(pdf, "/PageMode "); if (outline) { WriteLn(pdf, "/UseOutlines"); WriteLn(pdf, "/Outlines " + ObjNumberRef(outlineNumber)); } else { WriteLn(pdf, "/UseNone"); } if (isPdfA()) { WriteLn(pdf, "/Metadata " + ObjNumberRef(metaFileId)); if (embeddedFiles.Count > 0) { Write(pdf, "/AF " + ObjNumberRef(attachmentsListId)); WriteLn(pdf, " /Names << /EmbeddedFiles " + ObjNumberRef(attachmentsNamesId) + " >>"); } WriteLn(pdf, "/OutputIntents [ " + ObjNumberRef(colorProfileId) + " ]"); WriteLn(pdf, "/StructTreeRoot " + ObjNumberRef(structId)); } if (isPdfX()) { WriteLn(pdf, "/Metadata " + ObjNumberRef(metaFileId)); WriteLn(pdf, "/OutputIntents [ " + ObjNumberRef(colorProfileId) + " ]"); } WriteLn(pdf, "/ViewerPreferences <<"); if (displayDocTitle && !String.IsNullOrEmpty(title)) WriteLn(pdf, "/DisplayDocTitle true"); if (hideToolbar) WriteLn(pdf, "/HideToolbar true"); if (hideMenubar) WriteLn(pdf, "/HideMenubar true"); if (hideWindowUI) WriteLn(pdf, "/HideWindowUI true"); if (fitWindow) WriteLn(pdf, "/FitWindow true"); if (centerWindow) WriteLn(pdf, "/CenterWindow true"); if (!printScaling) WriteLn(pdf, "/PrintScaling false"); // /None WriteLn(pdf, ">>"); WriteLn(pdf, ">>"); WriteLn(pdf, "endobj"); startXRef = pdf.Position; WriteLn(pdf, "xref"); WriteLn(pdf, "0 " + (xRef.Count + 1).ToString()); WriteLn(pdf, "0000000000 65535 f"); foreach (long xref in xRef) WriteLn(pdf, PrepXRefPos(xref) + " 00000 n"); WriteLn(pdf, "trailer"); WriteLn(pdf, "<<"); WriteLn(pdf, "/Size " + (xRef.Count + 1).ToString()); WriteLn(pdf, "/Root " + ObjNumberRef(rootNumber)); WriteLn(pdf, "/Info " + ObjNumberRef(infoNumber)); WriteLn(pdf, "/ID [<" + fileID + "><" + fileID + ">]"); if (encrypted) { WriteLn(pdf, GetEncryptionDescriptor()); } WriteLn(pdf, ">>"); WriteLn(pdf, "startxref"); WriteLn(pdf, startXRef.ToString()); WriteLn(pdf, "%%EOF"); if (isDigitalSignEnable && digitalSignCertificate != null && signatureDictIndicies.byteRangeIndex != 0) { digitalSignByteRange = new long[4] { 0, signatureDictIndicies.contentsIndex - 1, signatureDictIndicies.contentsIndex + 16384 + 1, pdf.Length - signatureDictIndicies.contentsIndex - 16384 - 1 }; string byteRangeStr = string.Format("{0} {1} {2} {3} ]", digitalSignByteRange[0], digitalSignByteRange[1], digitalSignByteRange[2], digitalSignByteRange[3]); if (byteRangeStr.Length > 81) throw new OverflowException("ByteRange was bigger than 80 bytes"); pdf.Flush(); pdf.Seek(signatureDictIndicies.byteRangeIndex, SeekOrigin.Begin); byte[] arr = Encoding.ASCII.GetBytes(byteRangeStr); pdf.Write(arr, 0, byteRangeStr.Length); AddSignature(digitalSignCertificate); } } private void AddEmbeddedFileItem(EmbeddedFile file) { long fileRef = UpdateXRef(); WriteLn(pdf, ObjNumber(fileRef)); Write(pdf, "<< /Params << /ModDate (D:" + file.ModDate.ToString("yyyyMMddHHmmss") + ")"); Write(pdf, " /Size " + file.FileStream.Length.ToString()); WriteLn(pdf, " >>"); WriteLn(pdf, "/Subtype /" + file.MIME.Replace("/", "#2f")); WriteLn(pdf, "/Type /EmbeddedFile"); WritePDFStream(pdf, file.FileStream, fileRef, compressed, encrypted, false, true, true); long fileRel = UpdateXRef(); file.Xref = fileRel; WriteLn(pdf, ObjNumber(fileRel)); WriteLn(pdf, "<< /AFRelationship /" + file.Relation.ToString()); StringBuilder desc = new StringBuilder(); PrepareString(file.Description, encKey, encrypted, fileRel, desc); WriteLn(pdf, "/Desc " + desc.ToString()); Write(pdf, "/EF <<"); Write(pdf, " /F " + ObjNumberRef(fileRef)); Write(pdf, " /UF " + ObjNumberRef(fileRef)); WriteLn(pdf, " >>"); WriteLn(pdf, "/F (" + file.Name + ")"); WriteLn(pdf, "/Type /Filespec"); StringBuilder uf = new StringBuilder(); StrToUTF16(file.Name, uf); WriteLn(pdf, "/UF <" + uf.ToString() + ">"); WriteLn(pdf, ">>"); WriteLn(pdf, "endobj"); } private void AddAttachments() { if (embeddedFiles.Count > 0) { foreach (EmbeddedFile file in embeddedFiles) AddEmbeddedFileItem(file); attachmentsNamesId = UpdateXRef(); WriteLn(pdf, ObjNumber(attachmentsNamesId)); Write(pdf, "<< /Names ["); foreach (EmbeddedFile file in embeddedFiles) { Write(pdf, " (" + file.Name + ") "); Write(pdf, ObjNumberRef(file.Xref)); } WriteLn(pdf, " ] >>"); WriteLn(pdf, "endobj"); attachmentsListId = UpdateXRef(); WriteLn(pdf, ObjNumber(attachmentsListId)); Write(pdf, "[ "); foreach (EmbeddedFile file in embeddedFiles) Write(pdf, ObjNumberRef(file.Xref) + " "); WriteLn(pdf, "]"); WriteLn(pdf, "endobj"); } } private void AddStructure() { long roleMaps = UpdateXRef(); WriteLn(pdf, ObjNumber(roleMaps)); WriteLn(pdf, "<<\n/Footnote /Note\n/Endnote /Note\n/Textbox /Sect\n/Header /Sect\n/Footer /Sect\n/InlineShape /Sect\n/Annotation /Sect\n/Artifact /Sect\n/Workbook /Document\n/Worksheet /Part\n/Macrosheet /Part\n/Chartsheet /Part\n/Dialogsheet /Part\n/Slide /Part\n/Chart /Sect\n/Diagram /Figure\n>>\nendobj"); structId = UpdateXRef(); WriteLn(pdf, ObjNumber(structId)); WriteLn(pdf, "<<\n/Type /StructTreeRoot"); WriteLn(pdf, "/RoleMap " + ObjNumberRef(roleMaps)); // /ParentTree /K /ParentTreeNextKey WriteLn(pdf, ">>\nendobj"); } private void AddColorProfile(string GTS, string ICC) { string profileName = "default"; // color profile stream long FColorProfileStreamId = UpdateXRef(); WriteLn(pdf, ObjNumber(FColorProfileStreamId)); WriteLn(pdf, "<<"); WriteLn(pdf, "/N 3"); if (ColorProfile != null) { string pname = ParseICCFile(ColorProfile); if (pname != null) { profileName = pname.Trim('\0'); using (MemoryStream profileStream = new MemoryStream(ColorProfile)) { WritePDFStream(pdf, profileStream, FColorProfileStreamId, compressed, encrypted, false, true); } } } if (profileName == "default") { Assembly a = Assembly.GetExecutingAssembly(); using (Stream stream = a.GetManifestResourceStream("FastReport.Resources.Pdf." + ICC + ".icc")) { byte[] buf = new byte[stream.Length]; stream.Read(buf, 0, (int)stream.Length); string pname = ParseICCFile(buf); if (pname != null) profileName = pname.Trim('\0'); using (MemoryStream profileStream = new MemoryStream(buf)) { WritePDFStream(pdf, profileStream, FColorProfileStreamId, compressed, encrypted, false, true); } } } // color profile intent colorProfileId = UpdateXRef(); WriteLn(pdf, ObjNumber(colorProfileId)); WriteLn(pdf, "<<"); WriteLn(pdf, "/Type /OutputIntent"); WriteLn(pdf, "/S /GTS_" + GTS); WriteLn(pdf, ExportUtils.StringFormat("/OutputCondition ({0})", profileName)); WriteLn(pdf, ExportUtils.StringFormat("/OutputConditionIdentifier ({0})", profileName)); WriteLn(pdf, ExportUtils.StringFormat("/Info ({0})", profileName)); WriteLn(pdf, "/DestOutputProfile " + ObjNumberRef(FColorProfileStreamId)); WriteLn(pdf, ">>"); WriteLn(pdf, "endobj"); } private string ParseICCFile(byte[] file) { using (MemoryStream profileStream = new MemoryStream(file)) { profileStream.Position = 128; byte[] temp = new byte[4]; profileStream.Read(temp, 0, temp.Length); int count = temp[3];//limit to 255 tag count, for error avoid uint offset = 0;//if 0 then error uint size = 0;//size of desc byte[] desc = new byte[4] { 0x64, 0x65, 0x73, 0x63 }; for (int i = 0; i < count; i++) { //try to find desc tag profileStream.Read(temp, 0, temp.Length);//read signature bool flag_eq = true; for (int j = 0; j < temp.Length; j++) if (temp[j] != desc[j]) flag_eq = false; profileStream.Read(temp, 0, temp.Length);//read offset if (flag_eq) { offset = (uint)(temp[0] << 24) + (uint)(temp[1] << 16) + (uint)(temp[2] << 8) + (uint)(temp[3]); } profileStream.Read(temp, 0, temp.Length);//read lenght if (flag_eq) { size = (uint)(temp[0] << 24) + (uint)(temp[1] << 16) + (uint)(temp[2] << 8) + (uint)(temp[3]); break; } } if (offset == 0) return null; profileStream.Position = offset; profileStream.Read(temp, 0, temp.Length);//read signature for (int j = 0; j < temp.Length; j++) if (temp[j] != desc[j]) return null;//test 2 for desc error profileStream.Read(temp, 0, temp.Length);//read 0 profileStream.Read(temp, 0, temp.Length);//read lenght uint len = (uint)(temp[3]); byte[] result = new byte[len]; profileStream.Read(result, 0, result.Length); return Encoding.ASCII.GetString(result); } } private void AddMetaDataPdfA() { PDFMetaData pmd = new PDFMetaData(); pmd.Creator = Creator; pmd.Description = Subject; pmd.Keywords = Keywords; pmd.Title = Title; pmd.Producer = Producer; pmd.CreateDate = digitalSignCreationDate.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz"); pmd.DocumentID = fileID; pmd.InstanceID = fileID; pmd.ZUGFeRD = zUGFeRDDescription; switch (PdfCompliance) { default: pmd.Part = "2"; pmd.Conformance = "A"; break; case PdfStandard.PdfA_1a: pmd.Part = "1"; pmd.Conformance = "A"; break; case PdfStandard.PdfA_2b: pmd.Part = "2"; pmd.Conformance = "B"; break; case PdfStandard.PdfA_2u: pmd.Part = "2"; pmd.Conformance = "U"; break; case PdfStandard.PdfA_3a: pmd.Part = "3"; pmd.Conformance = "A"; break; case PdfStandard.PdfA_3b: pmd.Part = "3"; pmd.Conformance = "B"; break; } if (PdfCompliance == PdfStandard.PdfA_1a) { Author = Creator; } metaFileId = UpdateXRef(); WriteLn(pdf, ObjNumber(metaFileId)); WriteLn(pdf, "<< /Type /Metadata /Subtype /XML "); using (MemoryStream metaStream = new MemoryStream()) { ExportUtils.WriteLn(metaStream, pmd.MetaDataString); metaStream.Position = 0; WritePDFStream(pdf, metaStream, metaFileId, false, encrypted, false, true); } } private void AddMetaDataPdfX() { string metadata = null; if (PdfCompliance == PdfStandard.PdfX_3) metadata = "MetaDataX3"; else if (PdfCompliance == PdfStandard.PdfX_4) metadata = "MetaDataX4"; else throw new Exception("Error while adding metadata to PDF. Unknown PDF/X version: " + PdfCompliance.ToString()); // to pass adobe acrobat compliance test if (PdfCompliance == PdfStandard.PdfX_4) { Author = Creator; } PDFMetaData pmd = new PDFMetaData(metadata); pmd.Creator = Creator; pmd.Description = Subject; pmd.Keywords = Keywords; pmd.Title = Title; pmd.Producer = Producer; pmd.CreateDate = SystemFake.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:sszzz"); pmd.DocumentID = fileID; pmd.InstanceID = fileID; // to pass adobe acrobat compliance test if (PdfCompliance == PdfStandard.PdfX_4) { if (pmd.Title == null || pmd.Title.Trim() == "") pmd.Title = "FastReport.Net"; } metaFileId = UpdateXRef(); WriteLn(pdf, ObjNumber(metaFileId)); WriteLn(pdf, "<< /Type /Metadata /Subtype /XML "); using (MemoryStream metaStream = new MemoryStream()) { ExportUtils.WriteLn(metaStream, pmd.MetaDataString); metaStream.Position = 0; WritePDFStream(pdf, metaStream, metaFileId, false, encrypted, false, true); } } #endregion #region Protected Methods /// protected override string GetFileFilter() { return new MyRes("FileFilters").Get("PdfFile"); } /// protected override void Start() { base.Start(); graphics = Report.MeasureGraphics; xRef = new List(); pagesRef = new List(); pagesTopMargins = new List(); pagesHeights = new List(); fonts = new List(); hashList = new Dictionary(); pageAnnots = new StringBuilder(); annots = new List(); acroFormsRefs = new List(); acroFormsFonts = new List(); gradientHashSet = new Dictionary(); digitalSignCreationDate = SystemFake.DateTime.UtcNow; fileID = ExportUtils.GetID().Replace("-", ""); if (!String.IsNullOrEmpty(ownerPassword) || !String.IsNullOrEmpty(userPassword)) { encrypted = true; embeddingFonts = true; PrepareKeys(); } if (isDigitalSignEnable) { if (digitalSignCertificate == null && !String.IsNullOrEmpty(digitalSignCertificatePath)) { if (String.IsNullOrEmpty(digitalSignCertificatePassword)) digitalSignCertificate = new System.Security.Cryptography.X509Certificates .X509Certificate2(digitalSignCertificatePath); else digitalSignCertificate = new System.Security.Cryptography.X509Certificates .X509Certificate2(digitalSignCertificatePath, DigitalSignCertificatePassword); isDigitalSignatureInvisible = true; } if (digitalSignCertificate != null && !digitalSignCertificate.HasPrivateKey) { throw new InvalidOperationException("Certificate for signing is not valid, use another certificate or disable digital signature."); } else { isDigitalSignatureInvisible = true; } if (digitalSignCertificate == null) { //isDigitalSignEnable = false; isDigitalSignatureInvisible = true; } } if (useFileStream) { tempFileName = FileName + ".tmp"; pdf = new FileStream(tempFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose); } else pdf = new MemoryStream(); if (Report.PreparedPages.Outline.Xml.Count == 0) outline = false; AddPDFHeader(); if (showPrintDialog) { long FPrintDictJS = UpdateXRef(); WriteLn(pdf, ObjNumber(FPrintDictJS)); WriteLn(pdf, @"<>"); WriteLn(pdf, "endobj"); printDict = UpdateXRef(); WriteLn(pdf, ObjNumber(printDict)); WriteLn(pdf, "<>"); WriteLn(pdf, "endobj"); } } /*/// protected override void ExportPage(int pageNo) { using (ReportPage page = GetPage(pageNo)) { AddPage(page); ObjectCollection allObjects = page.AllObjects; for (int i = 0; i < allObjects.Count; i++) { ReportComponentBase c = allObjects[i] as ReportComponentBase; if (c != null) { c.Dispose(); c = null; } } } if (pageNo % 50 == 0) Application.DoEvents(); }*/ /// /// Begin exporting of page /// /// protected override void ExportPageBegin(ReportPage page) { if (ExportMode == ExportType.Export) base.ExportPageBegin(page); //reset shadings pageShadings = new List(); pageAlphaShading = new List(); //end reset shadings pageFonts = new List(); trasparentStroke = new List(); trasparentFill = new List(); picResList = new List(); //FAcroFormsAnnotsRefs = new List(); paperWidth = ExportUtils.GetPageWidth(page) * Units.Millimeters; paperHeight = ExportUtils.GetPageHeight(page) * Units.Millimeters; marginWoBottom = (ExportUtils.GetPageHeight(page) - page.TopMargin) * PDF_PAGE_DIVIDER; marginLeft = page.LeftMargin * PDF_PAGE_DIVIDER; pagesHeights.Add(marginWoBottom); pagesTopMargins.Add(page.TopMargin * PDF_PAGE_DIVIDER); contentsPos = 0; contentBuilder = new StringBuilder(65535); // page fill if (background) using (TextObject pageFill = new TextObject()) { pageFill.Fill = page.Fill; pageFill.Left = -marginLeft / PDF_DIVIDER; pageFill.Top = -page.TopMargin * PDF_PAGE_DIVIDER / PDF_DIVIDER; pageFill.Width = ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; pageFill.Height = ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER / PDF_DIVIDER; AddTextObject(pageFill, false, contentBuilder); } // bitmap watermark on bottom if (page.Watermark.Enabled && !page.Watermark.ShowImageOnTop) AddBitmapWatermark(page, contentBuilder); // text watermark on bottom if (page.Watermark.Enabled && !page.Watermark.ShowTextOnTop) AddTextWatermark(page, contentBuilder); // page borders if (page.Border.Lines != BorderLines.None) { using (TextObject pageBorder = new TextObject()) { pageBorder.Border = page.Border; pageBorder.Left = 0; pageBorder.Top = 0; pageBorder.Width = (ExportUtils.GetPageWidth(page) - page.LeftMargin - page.RightMargin) * PDF_PAGE_DIVIDER / PDF_DIVIDER; pageBorder.Height = (ExportUtils.GetPageHeight(page) - page.TopMargin - page.BottomMargin) * PDF_PAGE_DIVIDER / PDF_DIVIDER; AddTextObject(pageBorder, true, contentBuilder); } } } /// /// End exporting /// /// protected override void ExportPageEnd(ReportPage page) { base.ExportPageEnd(page); // bitmap watermark on top if (page.Watermark.Enabled && page.Watermark.ShowImageOnTop) AddBitmapWatermark(page, contentBuilder); // text watermark on top if (page.Watermark.Enabled && page.Watermark.ShowTextOnTop) AddTextWatermark(page, contentBuilder); // write page contentsPos = UpdateXRef(); WriteLn(pdf, ObjNumber(contentsPos)); using (MemoryStream tempContentStream = new MemoryStream()) { Write(tempContentStream, contentBuilder.ToString()); tempContentStream.Position = 0; WritePDFStream(pdf, tempContentStream, contentsPos, compressed, encrypted, true, true); } if (!textInCurves) { if (pageFonts.Count > 0) for (int i = 0; i < pageFonts.Count; i++) if (!pageFonts[i].Saved) { pageFonts[i].Reference = UpdateXRef(); pageFonts[i].Saved = true; } for (int i = 0; i < acroFormsFonts.Count; i++) { if (!fonts[acroFormsFonts[i]].Saved) { fonts[acroFormsFonts[i]].Reference = UpdateXRef(); fonts[acroFormsFonts[i]].Saved = true; } } } long PageNumber = UpdateXRef(); if (isDigitalSignEnable && pagesRef.Count == Report.PreparedPages.Count - 1 && isDigitalSignatureInvisible) { signatureDictIndicies = AddSignatureDict(null); UpdateXRef(PageNumber); } pagesRef.Add(PageNumber); WriteLn(pdf, ObjNumber(PageNumber)); StringBuilder sb = new StringBuilder(512); sb.AppendLine("<<").AppendLine("/Type /Page"); sb.Append("/MediaBox [0 0 ").Append(FloatToString(ExportUtils.GetPageWidth(page) * PDF_PAGE_DIVIDER)).Append(" "); sb.Append(FloatToString(ExportUtils.GetPageHeight(page) * PDF_PAGE_DIVIDER)).AppendLine(" ]"); //margins if (isPdfX()) sb.Append("/TrimBox [") .Append(FloatToString(page.LeftMargin * PDF_PAGE_DIVIDER)).Append(" ") .Append(FloatToString(page.TopMargin * PDF_PAGE_DIVIDER)).Append(" ") .Append(FloatToString(page.RightMargin * PDF_PAGE_DIVIDER)).Append(" ") .Append(FloatToString(page.BottomMargin * PDF_PAGE_DIVIDER)).Append("]"); sb.AppendLine("/Parent 1 0 R"); if (!isPdfX() && (PdfCompliance != PdfStandard.PdfA_1a)) { if (ColorSpace == PdfColorSpace.RGB) sb.AppendLine("/Group << /Type /Group /S /Transparency /CS /DeviceRGB >>"); else if (ColorSpace == PdfColorSpace.CMYK) sb.AppendLine("/Group << /Type /Group /S /Transparency /CS /DeviceCMYK >>"); } sb.AppendLine("/Resources << "); if (pageFonts.Count > 0) { sb.Append("/Font << "); foreach (ExportTTFFont font in pageFonts) sb.Append(font.Name).Append(" ").Append(ObjNumberRef(font.Reference)).Append(" "); sb.AppendLine(" >>"); } if (pageShadings != null && pageShadings.Count > 0) { sb.Append("/Shading <<"); for (int i = 0; i < pageShadings.Count; i++) sb.Append(" /sh").Append(i + 1).Append(" ").Append(ObjNumberRef(pageShadings[i])); //sb.Append(FPageShadings); sb.Append(" >>"); } sb.AppendLine("/ExtGState <<"); for (int i = 0; i < trasparentStroke.Count; i++) sb.Append("/GS").Append(i.ToString()).Append("S << /Type /ExtGState /ca ").Append(trasparentStroke[i]).AppendLine(" >>"); for (int i = 0; i < trasparentFill.Count; i++) sb.Append("/GS").Append(i.ToString()).Append("F << /Type /ExtGState /CA ").Append(trasparentFill[i]).AppendLine(" >>"); for (int i = 0; i < pageAlphaShading.Count; i++) { if (isPdfX()) { // /s6 << /ca 1 /Type /ExtGState /AIS false /SMask << /Type /Mask /G << >> /S /Luminosity >> /CA 1 >> sb.Append("/s").Append(i + 1).AppendLine(" << /ca 1 /Type /ExtGState /CA 1 >>"); } else { // /s6 << /ca 1 /Type /ExtGState /AIS false /SMask << /Type /Mask /G << >> /S /Luminosity >> /CA 1 >> sb.Append("/s").Append(i + 1).Append(" << /ca 1 /Type /ExtGState /AIS false /SMask << /Type /Mask /G ") .Append(ObjNumberRef(pageAlphaShading[i])).AppendLine(" /S /Luminosity >> /CA 1 >>"); } } sb.AppendLine(">>"); if (picResList.Count > 0) { sb.Append("/XObject << "); foreach (long resIndex in picResList) sb.Append("/Im").Append(resIndex.ToString()).Append(" ").Append(ObjNumberRef(resIndex)).Append(" "); sb.AppendLine(" >>"); } sb.AppendLine("/ProcSet [/PDF /Text /ImageC ]"); sb.AppendLine(">>"); sb.Append("/Contents ").AppendLine(ObjNumberRef(contentsPos)); if (pageAnnots.Length > 0) { sb.AppendLine(GetPageAnnots()); pageAnnots.Length = 0; } sb.AppendLine(">>"); sb.AppendLine("endobj"); Write(pdf, sb.ToString()); } /// /// Export of Band /// /// protected override void ExportBand(BandBase band) { if (ExportMode == ExportType.Export) base.ExportBand(band); ExportObj(band); foreach (Base c in band.ForEachAllConvectedObjects(this)) { ExportObj(c); } } private void ExportObj(Base c) { if (c is ReportComponentBase && (c as ReportComponentBase).Exportable) { ReportComponentBase obj = c as ReportComponentBase; if (obj is CellularTextObject) obj = (obj as CellularTextObject).GetTable(); if (obj is TableCell) return; #if DOTNET_4 else if (PDFExportV4(obj)) { } #endif else if (obj is TableBase) { //using (TableBase table = obj as TableBase) { if ((obj as TableBase).ColumnCount > 0 && (obj as TableBase).RowCount > 0) { StringBuilder tableBorder = new StringBuilder(64); using (TextObject tableback = new TextObject()) { tableback.Border = (obj as TableBase).Border; tableback.Fill = (obj as TableBase).Fill; tableback.FillColor = (obj as TableBase).FillColor; tableback.Left = (obj as TableBase).AbsLeft; tableback.Top = (obj as TableBase).AbsTop; float tableWidth = 0; float tableHeight = 0; for (int i = 0; i < (obj as TableBase).ColumnCount; i++) tableWidth += (obj as TableBase).Columns[i].Width;// table[i, 0].Width; for (int i = 0; i < (obj as TableBase).RowCount; i++) tableHeight += (obj as TableBase).Rows[i].Height; tableback.Width = tableWidth;// (tableWidth < table.Width) ? tableWidth : table.Width; tableback.Height = tableHeight; AddTextObject(tableback, false, contentBuilder); DrawPDFBorder(tableback.Border, tableback.AbsLeft, tableback.AbsTop, tableback.Width, tableback.Height, tableBorder); } // draw cells AddTable((obj as TableBase), true, contentBuilder); // draw cells border AddTable((obj as TableBase), false, contentBuilder); // draw table border contentBuilder.Append(tableBorder); } } } else if (obj is TextObject) AddTextObject(obj as TextObject, true, contentBuilder); else if (obj is BandBase) AddBandObject(obj as BandBase, contentBuilder); else if (obj is LineObject) AddLine(obj as LineObject, contentBuilder); else if (obj is ShapeObject) AddShape(obj as ShapeObject, contentBuilder); else if (obj is RichObject) AddPictureObject(obj, true, richTextQuality, contentBuilder); else if (obj is PolyLineObject) AddPDFPolylineVector(obj as PolyLineObject, contentBuilder); //AddPolyLine(obj as PolyLineObject, contentBuilder); else if (obj is CheckBoxObject && (obj as CheckBoxObject).Editable && InteractiveForms) AddCheckBoxField(obj as CheckBoxObject); else if (!(obj is HtmlObject)) AddPictureObject(obj, true, jpegQuality, contentBuilder); if (obj is DigitalSignatureObject && isDigitalSignEnable) { AddSignatureDict(obj); if (digitalSignCertificate == null) { isDigitalSignatureInvisible = false; } } } } /// protected override void Finish() { AddPDFFooter(); for (int i = fonts.Count - 1; i >= 0; i--) fonts[i].Dispose(); if (useFileStream) { pdf.Close(); if (File.Exists(FileName)) { Stream.Close(); File.Delete(FileName); File.Move(tempFileName, FileName); } else File.Delete(tempFileName); } else ((MemoryStream)pdf).WriteTo(Stream); } #endregion #region Public Methods /// public override void Serialize(FRWriter writer) { base.Serialize(writer); // Options writer.WriteValue("PdfCompliance", PdfCompliance); writer.WriteBool("EmbeddingFonts", EmbeddingFonts); writer.WriteBool("Background", Background); writer.WriteBool("TextInCurves", TextInCurves); writer.WriteValue("ColorSpace", ColorSpace); writer.WriteBool("ImagesOriginalResolution", ImagesOriginalResolution); writer.WriteBool("PrintOptimized", PrintOptimized); writer.WriteBool("JpegCompression", JpegCompression); writer.WriteInt("JpegQuality", JpegQuality); writer.WriteBool("InteractiveForms", InteractiveForms); // end // Document Information writer.WriteStr("Title", Title); writer.WriteStr("Author", Author); writer.WriteStr("Subject", Subject); writer.WriteStr("Keywords", Keywords); writer.WriteStr("Creator", Creator); writer.WriteStr("Producer", Producer); // end // Security writer.WriteBool("AllowPrint", AllowPrint); writer.WriteBool("AllowModify", AllowModify); writer.WriteBool("AllowCopy", AllowCopy); writer.WriteBool("AllowAnnotate", AllowAnnotate); // end // Viewer writer.WriteBool("AutoPrint", ShowPrintDialog); writer.WriteBool("HideToolbar", HideToolbar); writer.WriteBool("HideMenubar", HideMenubar); writer.WriteBool("HideWindowUI", HideWindowUI); writer.WriteBool("FitWindow", FitWindow); writer.WriteBool("CenterWindow", CenterWindow); writer.WriteBool("PrintScaling", PrintScaling); writer.WriteBool("Outline", Outline); writer.WriteValue("DefaultZoom", DefaultZoom); // end // Curves writer.WriteValue("GradientInterpolationPoints", GradientInterpolationPoints); writer.WriteValue("GradientQuality", GradientQuality); writer.WriteValue("CurvesInterpolation", CurvesInterpolation); writer.WriteValue("CurvesInterpolationText", CurvesInterpolationText); writer.WriteBool("SvgAsPicture", SvgAsPicture); writer.WriteBool("SaveDigitalSignCertificatePassword", SaveDigitalSignCertificatePassword); writer.WriteBool("IsDigitalSignEnable", IsDigitalSignEnable); writer.WriteValue("DigitalSignCertificatePath", DigitalSignCertificatePath); writer.WriteValue("DigitalSignLocation", DigitalSignLocation); writer.WriteValue("DigitalSignContactInfo", DigitalSignContactInfo); writer.WriteValue("DigitalSignReason", DigitalSignReason); if (SaveDigitalSignCertificatePassword) { writer.WriteValue("DigitalSignCertificatePassword", digitalSignCertificatePassword); } else { writer.WriteValue("DigitalSignCertificatePassword", null); } // end } /// /// Add an embedded XML file (only for PDF/A-3 standard). /// /// File name /// Description /// Modification date /// File stream public void AddEmbeddedXML(string name, string description, DateTime modDate, Stream fileStream) { AddEmbeddedXML(name, description, modDate, fileStream, ZUGFeRD_ConformanceLevel.BASIC); } /// /// Add an embedded XML file (only for PDF/A-3 standard). /// /// File name /// Description /// Modification date /// File stream /// ZUGFeRD Conformance Level public void AddEmbeddedXML(string name, string description, DateTime modDate, Stream fileStream, ZUGFeRD_ConformanceLevel ZUGFeRDLevel) { zUGFeRDDescription = ExportUtils.StringFormat("", ZUGFeRDLevel.ToString(), name); AddEmbeddedFile(name, description, modDate, EmbeddedRelation.Alternative, "text/xml", fileStream); } /// /// Add an embedded file (only for PDF/A-3 standard). /// /// File name /// Description /// Modification date /// Relation type /// MIME type /// File stream public void AddEmbeddedFile(string name, string description, DateTime modDate, EmbeddedRelation relation, string mime, Stream fileStream) { EmbeddedFile file = new EmbeddedFile(); file.Name = name; file.Description = description; file.ModDate = modDate; file.Relation = relation; file.MIME = mime; file.FileStream = fileStream; embeddedFiles.Add(file); } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public PDFExport() { embeddedFiles = new List(); exportMode = ExportType.Export; embeddingFonts = true; useFileStream = false; } #endregion } }