/// <summary> /// Add namespace declaration for the specified namespace URI. /// </summary> /// <exception cref="FormatException"> /// Unable to add standard namespace declaration at root element. /// </exception> public static string AddStdNamespaceDeclaration(XmlElement root, string prefix_header, string nsURI) { int limit = 1000; // hard-coded upper limit for namespace prefix string look up int index; string prefix = null; // We need to find a prefix string that doesn't conflict with any prefixes already used. for (index = 0; index < limit; index++) { // the new prefix is in the format like: "psk0000" "xsi0001" "xsd0100" prefix = prefix_header + index.ToString(CultureInfo.InvariantCulture).PadLeft(4, '0'); if (root.Attributes["xmlns:" + prefix] == null) { root.SetAttribute("xmlns:" + prefix, nsURI); break; } } if (index >= limit) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.AddXmlnsFailAtRootElement"))); } return(prefix); }
/// <summary> /// Returns a new FormatException instance for not-well-formed PrintCapabilities XML. /// </summary> /// <param name="detailMsg">detailed message about the violation of well-formness</param> /// <param name="innerException">the exception that causes the violation of well-formness</param> /// <returns>the new FormatException instance</returns> internal static FormatException NewPrintCapFormatException(string detailMsg, Exception innerException) { return(new FormatException(String.Format(CultureInfo.CurrentCulture, "{0} {1} {2}", PrintSchemaTags.Framework.PrintCapRoot, PTUtility.GetTextFromResource("FormatException.XMLNotWellFormed"), detailMsg), innerException)); }
/// <summary> /// Gets current Property/ScoredProperty's ParameterRef name from its "ParameterRef" child-element. /// The returned ParameterRef name is guaranteed to be in the standard keyword set namespace. /// </summary> /// <exception cref="FormatException">either can't find the child-element or its name is private</exception> /// <exception cref="XmlException">XML is not well-formed.</exception> public string GetCurrentPropertyParamRefNameWithException() { if (!MoveToNextSchemaElement(CurrentElementDepth + 1, PrintSchemaNodeTypes.ParameterRef)) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.MissingRequiredChildElement"), PrintSchemaTags.Framework.ParameterRef, _xmlReader.LineNumber, _xmlReader.LinePosition)); } return(CurrentElementNameAttrValue); }
/// <summary> /// Gets current Property/ScoredProperty's full text value from its "Value" child-element. /// </summary> /// <exception cref="FormatException">can't find the value</exception> /// <exception cref="XmlException">XML is not well-formed.</exception> public string GetCurrentPropertyFullValueWithException() { // No need to loop here. We just need to look for the first <Value> child-element if (!MoveToNextSchemaElement(CurrentElementDepth + 1, PrintSchemaNodeTypes.Value)) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.MissingRequiredChildElement"), PrintSchemaTags.Framework.Value, _xmlReader.LineNumber, _xmlReader.LinePosition)); } return(CurrentElementTextValue); }
/// <summary> /// Gets the PrintCapabilities relative to the given PrintTicket. /// </summary> /// <param name="printTicket">The stream that contains XML PrintTicket based on which PrintCapabilities should be built.</param> /// <returns>Stream that contains XML PrintCapabilities.</returns> /// <exception cref="ObjectDisposedException"> /// The PTProvider instance has already been disposed. /// </exception> /// <exception cref="ArgumentException"> /// The input PrintTicket specified by <paramref name="printTicket"/> is not well-formed. /// </exception> /// <exception cref="PrintQueueException"> /// The PTProvider instance failed to retrieve the PrintCapabilities. /// </exception> /// <exception cref="PrintingNotSupportedException"> /// Printing components are not installed on the client /// </exception> public override MemoryStream GetPrintCapabilities(MemoryStream printTicket) { VerifyAccess(); IStream printCapabilitiesStream = CreateStreamOnHGlobal(); try { IStream printTicketStream = IStreamFromMemoryStream(printTicket); try { string errorMsg; // What happens if the native code returns a iStreamLength that is too long? // One option is we trust providers and don't do run-time check. uint hResult = UnsafeNativeMethods.PTGetPrintCapabilities(_providerHandle, printTicketStream, printCapabilitiesStream, out errorMsg); if (PTUtility.IsSuccessCode(hResult)) { RewindIStream(printCapabilitiesStream); return(MemoryStreamFromIStream(printCapabilitiesStream)); } if (hResult == (uint)NativeErrorCode.E_PRINTTICKET_FORMAT) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "{0} {1} {2}", PrintSchemaTags.Framework.PrintTicketRoot, PTUtility.GetTextFromResource("FormatException.XMLNotWellFormed"), errorMsg), "printTicket"); } else { throw new PrintQueueException((int)hResult, "PrintConfig.Provider.GetPrintCapFail", _deviceName, errorMsg); } } finally { DeleteIStream(ref printTicketStream); } } finally { DeleteIStream(ref printCapabilitiesStream); } }
/// <summary> /// Post-process states populated by the builder and populate aggregates states /// </summary> /// <exception cref="FormatException">thrown if XML PrintCapabilities is not well-formed</exception> private void PostBuildProcessing() { for (int i = 0; i < _countLocalParamDefs; i++) { if (_baLocalParamRequired[i]) { // If a parameter definition has be referenced, then the parameter definition must be present in the XML. if (_pcLocalParamDefs[i] == null) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.ParameterDefMissOrInvalid"), PrintSchemaTags.Framework.ParameterDef, (PrintSchemaLocalParameterDefs)i)); } } } }
/// <summary> /// Sets page scaling setting to custom square scaling. /// </summary> /// <param name="squareScale">custom square scaling percentage</param> /// <exception cref="ArgumentOutOfRangeException"> /// "squareScale" is not non-negative value. /// </exception> public void SetCustomSquareScaling(int squareScale) { // Scaling percentage value must be non-negative. We do allow negative scaling offset values. if (squareScale < 0) { throw new ArgumentOutOfRangeException("squareScale", PTUtility.GetTextFromResource("ArgumentException.NonNegativeValue")); } // Remove all XML content related to PageScaling feature // any way to avoid redundant NotifyPropertyChanged calls? ClearSetting(); this[PrintSchemaTags.Framework.OptionNameProperty] = (int)PageScaling.CustomSquare; this[PrintSchemaTags.Keywords.PageScalingKeys.CustomSquareScale] = squareScale; }
/// <summary> /// Gets current Property/ScoredProperty's QName LocalName value from its "Value" child-element. /// The returned property QName value is guaranteed to be in the standard keyword set namespace. /// </summary> /// <exception cref="FormatException">either can't find the value or value is private</exception> /// <exception cref="XmlException">XML is not well-formed.</exception> public string GetCurrentPropertyQNameValueWithException() { string QName = GetCurrentPropertyFullValueWithException(); if (XmlReaderQName.GetURI(_xmlReader, QName) == PrintSchemaNamespaces.StandardKeywordSet) { return(XmlReaderQName.GetLocalName(QName)); } else { // Needs to handle private XML text value throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.PrivateXMLTextValue"), QName, _xmlReader.LineNumber, _xmlReader.LinePosition)); } }
/// <exception cref="ArgumentOutOfRangeException"> /// "value" is not one of the standard <see cref="PageMediaSizeName"/>. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// Either "mediaSizeWidth" or "mediaSizeHeight" is not a positive dimension value. /// </exception> private void InternalSetFixedMediaSize(PageMediaSizeName value, bool bSetValue, double mediaSizeWidth, double mediaSizeHeight, bool bSetWH) { if (bSetValue) { if (value < PrintSchema.PageMediaSizeNameEnumMin || value > PrintSchema.PageMediaSizeNameEnumMax) { throw new ArgumentOutOfRangeException("value"); } } if (bSetWH) { if ((mediaSizeWidth <= 0) || (mediaSizeHeight <= 0)) { throw new ArgumentOutOfRangeException((mediaSizeWidth <= 0) ? "mediaSizeWidth" : "mediaSizeHeight", PTUtility.GetTextFromResource("ArgumentException.PositiveValue")); } } // Remove all XML content related to PageMediaSize feature ClearSetting(); if (bSetValue) { this[PrintSchemaTags.Framework.OptionNameProperty] = (int)value; } if (bSetWH) { this[PrintSchemaTags.Keywords.PageMediaSizeKeys.MediaSizeWidth] = UnitConverter.LengthValueFromDIPToMicron(mediaSizeWidth); this[PrintSchemaTags.Keywords.PageMediaSizeKeys.MediaSizeHeight] = UnitConverter.LengthValueFromDIPToMicron(mediaSizeHeight); } }
/// <summary> /// Gets current Property/ScoredProperty's integer value from its "Value" child-element /// </summary> /// <exception cref="FormatException">either can't find the value or find invalid value number</exception> /// <exception cref="XmlException">XML is not well-formed.</exception> public int GetCurrentPropertyIntValueWithException() { // Do the assignment outside of try-catch so the FormatException of value-not-found could be thrown properly. string textValue = GetCurrentPropertyFullValueWithException(); int intValue = 0; try { intValue = XmlConvertHelper.ConvertStringToInt32(textValue); } catch (FormatException e) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.InvalidXMLIntValue"), CurrentElementTextValue, _xmlReader.LineNumber, _xmlReader.LinePosition), e); } return(intValue); }
/// <summary> /// Instantiates a reader object for the given XML PrintCapabilities /// </summary> /// <remarks>Constructor verifies the root element is valid</remarks> /// <exception cref="FormatException">thrown if XML PrintCapabilities is not well-formed</exception> public XmlPrintCapReader(Stream xmlStream) { // Internally the XML PrintCapabilities reader uses XmlTextReader _xmlReader = new XmlTextReader(xmlStream); // We need namespace support from the reader. _xmlReader.Namespaces = true; // Don't resolve external resources. _xmlReader.XmlResolver = null; // Verify root element is <PrintCapabilities> in our standard namespace if ((_xmlReader.MoveToContent() != XmlNodeType.Element) || (_xmlReader.LocalName != PrintSchemaTags.Framework.PrintCapRoot) || (_xmlReader.NamespaceURI != PrintSchemaNamespaces.Framework)) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.InvalidRootElement"), _xmlReader.NamespaceURI, _xmlReader.LocalName)); } // Verify the XML PrintCapabilities version is supported // For XML attribute without a prefix (e.g. <... name="prn:PageMediaSize">), // even though the XML document has default namespace defined as our standard // Print Schema framework namespace, the XML atribute still has NULL namespaceURI. // It will only have the correct namespaceURI when a prefix is used. This doesn't // apply to XML element, whose namespaceURI works fine with default namespace. // GetAttribute doesn't move the reader cursor away from the current element string version = _xmlReader.GetAttribute(PrintSchemaTags.Framework.RootVersionAttr, PrintSchemaNamespaces.FrameworkAttrForXmlReader); if (version == null) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootMissingAttribute"), PrintSchemaTags.Framework.RootVersionAttr)); } // Convert string to number to verify decimal versionNum; try { versionNum = XmlConvertHelper.ConvertStringToDecimal(version); } catch (FormatException e) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootInvalidAttribute"), PrintSchemaTags.Framework.RootVersionAttr, version), e); } if (versionNum != PrintSchemaTags.Framework.SchemaVersion) { throw NewPrintCapFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.VersionNotSupported"), versionNum)); } // Reset internal states to be ready for client's reading of the PrintCapabilities XML ResetCurrentElementState(); }
/// <summary> /// Converts the given PrintTicket into Win32 DEVMODE. /// </summary> /// <param name="printTicket">MemoryStream containing the XML PrintTicket.</param> /// <param name="baseType">Type of default DEVMODE to use as base of conversion.</param> /// <param name="scope">scope that the input PrintTicket will be limited to</param> /// <returns>Byte buffer that contains the converted Win32 DEVMODE.</returns> /// <exception cref="ObjectDisposedException"> /// The PTProvider instance has already been disposed. /// </exception> /// <exception cref="ArgumentException"> /// The PrintTicket specified by <paramref name="printTicket"/> is not well-formed. /// </exception> /// <exception cref="PrintQueueException"> /// The PTProvider instance failed to convert the PrintTicket to a DEVMODE. /// </exception> /// <exception cref="PrintingNotSupportedException"> /// Printing components are not installed on the client /// </exception> public override byte[] ConvertPrintTicketToDevMode(MemoryStream printTicket, BaseDevModeType baseType, PrintTicketScope scope) { VerifyAccess(); uint hResult = 0; string errorMsg = null; IntPtr umDevMode = IntPtr.Zero; try { IStream printTicketStream = IStreamFromMemoryStream(printTicket); try { uint umDevModeLen = 0; hResult = UnsafeNativeMethods.PTConvertPrintTicketToDevMode(_providerHandle, printTicketStream, (uint)baseType, (uint)scope, out umDevModeLen, out umDevMode, out errorMsg); if (PTUtility.IsSuccessCode(hResult)) { byte[] devMode = new byte[umDevModeLen]; Marshal.Copy(umDevMode, devMode, 0, (int)umDevModeLen); return(devMode); } } finally { DeleteIStream(ref printTicketStream); } } finally { if (umDevMode != IntPtr.Zero) { UnsafeNativeMethods.PTReleaseMemory(new HandleRef(this, umDevMode)); umDevMode = IntPtr.Zero; } } if ((hResult == (uint)NativeErrorCode.E_XML_INVALID) || (hResult == (uint)NativeErrorCode.E_PRINTTICKET_FORMAT)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "{0} {1} {2}", PrintSchemaTags.Framework.PrintTicketRoot, PTUtility.GetTextFromResource("FormatException.XMLNotWellFormed"), errorMsg), "printTicket"); } throw new PrintQueueException((int)hResult, "PrintConfig.Provider.PT2DevModeFail", _deviceName, errorMsg); }
/// <summary> /// Merges delta PrintTicket with base PrintTicket and then validates the merged PrintTicket. /// </summary> /// <param name="basePrintTicket">The MemoryStream that contains base XML PrintTicket.</param> /// <param name="deltaPrintTicket">The MemoryStream that contains delta XML PrintTicket.</param> /// <param name="scope">scope that delta PrintTicket and result PrintTicket will be limited to</param> /// <param name="conflictStatus">The returned conflict resolving status.</param> /// <returns>MemoryStream that contains validated and merged PrintTicket XML.</returns> /// <exception cref="ObjectDisposedException"> /// The PTProvider instance has already been disposed. /// </exception> /// <exception cref="ArgumentException"> /// The base PrintTicket specified by <paramref name="basePrintTicket"/> is not well-formed, /// or delta PrintTicket specified by <paramref name="deltaPrintTicket"/> is not well-formed. /// </exception> /// <exception cref="PrintQueueException"> /// The PTProvider instance failed to merge and validate the input PrintTicket(s). /// </exception> /// <exception cref="PrintingNotSupportedException"> /// Printing components are not installed on the client /// </exception> public override MemoryStream MergeAndValidatePrintTicket(MemoryStream basePrintTicket, MemoryStream deltaPrintTicket, PrintTicketScope scope, out ConflictStatus conflictStatus) { VerifyAccess(); conflictStatus = ConflictStatus.NoConflict; IStream validatedPrintTicketStream = CreateStreamOnHGlobal(); try { IStream baseTicketStream = IStreamFromMemoryStream(basePrintTicket); try { IStream deltaTicketStream = IStreamFromMemoryStream(deltaPrintTicket); try { string errorMsg; uint hResult = UnsafeNativeMethods.PTMergeAndValidatePrintTicket(_providerHandle, baseTicketStream, deltaTicketStream, (uint)scope, validatedPrintTicketStream, out errorMsg); if (PTUtility.IsSuccessCode(hResult)) { // convert the success hResult to an enum value switch (hResult) { case (uint)NativeErrorCode.S_PT_CONFLICT_RESOLVED: { conflictStatus = ConflictStatus.ConflictResolved; break; } case (uint)NativeErrorCode.S_PT_NO_CONFLICT: { conflictStatus = ConflictStatus.NoConflict; break; } default: { throw new PrintQueueException((int)hResult, "PrintConfig.Provider.MergeValidateFail", _deviceName); } } RewindIStream(validatedPrintTicketStream); return(MemoryStreamFromIStream(validatedPrintTicketStream)); } if ((hResult == (uint)NativeErrorCode.E_PRINTTICKET_FORMAT) || (hResult == (uint)NativeErrorCode.E_DELTA_PRINTTICKET_FORMAT)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "{0} {1} {2}", PrintSchemaTags.Framework.PrintTicketRoot, PTUtility.GetTextFromResource("FormatException.XMLNotWellFormed"), errorMsg), (hResult == (uint)NativeErrorCode.E_PRINTTICKET_FORMAT) ? "basePrintTicket" : "deltaPrintTicket"); } else { throw new PrintQueueException((int)hResult, "PrintConfig.Provider.MergeValidateFail", _deviceName, errorMsg); } } finally { DeleteIStream(ref deltaTicketStream); } } finally { DeleteIStream(ref baseTicketStream); } } finally { DeleteIStream(ref validatedPrintTicketStream); } }
/// <summary> /// Verifies if the PrintTicket is well-formed /// </summary> /// <exception cref="FormatException"> /// The PrintTicket is not well-formed. /// </exception> public static void CheckIsWellFormedPrintTicket(InternalPrintTicket pt) { XmlElement root = pt.XmlDoc.DocumentElement; // Root element should be in our standard namespace and should be <PrintTicket>. if ((root.NamespaceURI != PrintSchemaNamespaces.Framework) || (root.LocalName != PrintSchemaTags.Framework.PrintTicketRoot)) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.InvalidRootElement"), root.NamespaceURI, root.LocalName)); } string version = root.GetAttribute(PrintSchemaTags.Framework.RootVersionAttr, PrintSchemaNamespaces.FrameworkAttrForXmlDOM); // Root element should have the "version" attribute // (XmlElement.GetAttribute returns empty string when the attribute is not found, but // (XmlTextReader.GetAttribute returns null when the attribute is not found) if ((version == null) || (version.Length == 0)) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootMissingAttribute"), PrintSchemaTags.Framework.RootVersionAttr)); } decimal versionNum; try { versionNum = XmlConvertHelper.ConvertStringToDecimal(version); } catch (FormatException e) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootInvalidAttribute"), PrintSchemaTags.Framework.RootVersionAttr, version), e); } // and the "version" attribute value should be what we support if (versionNum != PrintSchemaTags.Framework.SchemaVersion) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.VersionNotSupported"), versionNum)); } // Now go through each root child element and verify they are valid children XmlNode rootChild = root.FirstChild; // It's recommended that traversing the node in forward-only movement by using NextSibling // is best for XmlDocument performance. This is because the list is not double-linked. while (rootChild != null) { // If the root child doesn't live in our standard namespace, we should ignore it // rather than rejecting it since it's acceptable to have private elements under // the root. if (rootChild.NamespaceURI == PrintSchemaNamespaces.Framework) { // For <PrintTicket> root, our Framework schema only allow these children elements: // <Feature> <AttributeSet> <Property> <ParameterInit> if ((rootChild.NodeType != XmlNodeType.Element) || ((rootChild.LocalName != PrintSchemaTags.Framework.Feature) && (rootChild.LocalName != PrintSchemaTags.Framework.ParameterInit) && (rootChild.LocalName != PrintSchemaTags.Framework.Property))) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootInvalidChildElement"), rootChild.Name)); } string childName = ((XmlElement)rootChild).GetAttribute(PrintSchemaTags.Framework.NameAttr, PrintSchemaNamespaces.FrameworkAttrForXmlDOM); // All the recognized root child element should have an XML attribut "name" if ((childName == null) || (childName.Length == 0)) { throw NewPTFormatException(String.Format(CultureInfo.CurrentCulture, PTUtility.GetTextFromResource("FormatException.RootChildMissingAttribute"), rootChild.Name, PrintSchemaTags.Framework.NameAttr)); } } rootChild = rootChild.NextSibling; } // We will end the verification at the root child level here. Instead of traversing the whole tree // to find violations in this construtor, we will delay detecting violations under root child level // until information of an individual feature/property subtree is requested. }
/// <summary> /// Gets the PrintCapabilities relative to the given PrintTicket. /// </summary> /// <param name="printTicket">The stream that contains XML PrintTicket based on which PrintCapabilities should be built.</param> /// <returns>Stream that contains XML PrintCapabilities.</returns> /// <exception cref="ObjectDisposedException"> /// The PTProvider instance has already been disposed. /// </exception> /// <exception cref="ArgumentException"> /// The input PrintTicket specified by <paramref name="printTicket"/> is not well-formed. /// </exception> /// <exception cref="PrintQueueException"> /// The PTProvider instance failed to retrieve the PrintCapabilities. /// </exception> public override MemoryStream GetPrintCapabilities(MemoryStream printTicket) { VerifyAccess(); InternalPrintTicket internalTicket = null; try { internalTicket = (printTicket != null) ? new InternalPrintTicket(printTicket) : null; } catch (XmlException xmlException) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, "{0} {1} {2}", PrintSchemaTags.Framework.PrintTicketRoot, PTUtility.GetTextFromResource("FormatException.XMLNotWellFormed"), xmlException.Message), "printTicket", xmlException); } DevMode defaultDevMode = GetDEVMODE(BaseDevModeType.UserDefault); DevMode devMode = defaultDevMode.Clone(); PrintTicketToDevMode(devMode, internalTicket, PrintTicketScope.JobScope, DevModeFields.All); MemoryStream capabilitiesStream = new MemoryStream(); WinSpoolPrinterCapabilities capabilities = GetCapabilities(devMode); try { PrintCapabilitiesWriter writer = new PrintCapabilitiesWriter(capabilitiesStream, "ns0000", this.OemDriverNamespace, false); try { writer.WriteStartDocument(); { writer.WritePageDevmodeSnapshot(devMode.ByteData); if (capabilities.HasICMIntent) { writer.WritePageICMRenderingIntentFeature(); } if (capabilities.HasICMMethod) { writer.WritePageColorManagementFeature(); } if (capabilities.CanCollate) { writer.WriteDocumentCollateFeature(); } int minCopies = capabilities.MinCopies; int maxCopies = capabilities.MaxCopies; int defaultCopies = minCopies; defaultCopies = Math.Max(minCopies, Math.Min(defaultDevMode.Copies, maxCopies)); writer.WriteJobCopiesAllDocumentsParameterDef(minCopies, maxCopies, defaultCopies); writer.WriteJobNUpAllDocumentsContiguously(capabilities.NUp); writer.WriteJobDuplexAllDocumentsContiguouslyFeature(capabilities.CanDuplex); if (capabilities.CanScale) { writer.WritePageScalingFeature(1, 1000, 100); } writer.WritePageMediaSizeFeature( capabilities.Papers, capabilities.PaperNames, PrintSchemaShim.TenthOfMillimeterToMicrons(capabilities.PaperSizes) ); writer.WritePageResolutionFeature(capabilities.Resolutions); int logicalPixelsX, logicalPixelsY; int physicalWidth, physicalHeight; int physicalOffsetX, physicalOffsetY; int horizontalResolution, verticalResolution; bool gotDevCaps = capabilities.TryGetDeviceCapabilities( out logicalPixelsX, out logicalPixelsY, out physicalWidth, out physicalHeight, out physicalOffsetX, out physicalOffsetY, out horizontalResolution, out verticalResolution); if (gotDevCaps && logicalPixelsX > 0 && logicalPixelsY > 0) { int imageableSizeWidth = PrintSchemaShim.DpiToMicrons(physicalWidth, logicalPixelsX); int imageableSizeHeight = PrintSchemaShim.DpiToMicrons(physicalHeight, logicalPixelsY); int originWidth = PrintSchemaShim.DpiToMicrons(physicalOffsetX, logicalPixelsY); int originHeight = PrintSchemaShim.DpiToMicrons(physicalOffsetY, logicalPixelsY); int extentWidth = PrintSchemaShim.DpiToMicrons(horizontalResolution, logicalPixelsY); int extentHeight = PrintSchemaShim.DpiToMicrons(verticalResolution, logicalPixelsY); writer.WritePageImageableSizeProperty( imageableSizeWidth, imageableSizeHeight, originWidth, originHeight, extentWidth, extentHeight); } writer.WritePageOrientationFeature(capabilities.LandscapeOrientation); writer.WritePageOutputColorFeature(capabilities.HasColor); writer.WriteJobInputBinFeature(capabilities.Bins, capabilities.BinNames); writer.WritePageMediaTypeFeature(capabilities.MediaTypes, capabilities.MediaTypeNames); if (capabilities.TrueType) { writer.WritePageTrueTypeFontModeFeature(); writer.WritePageDeviceFontSubstitutionFeature(); } } writer.WriteEndDocument(); } finally { writer.Release(); writer = null; } } finally { capabilities.Release(); capabilities = null; } // calls Security Critical Dispose on a private resource capabilitiesStream.Position = 0; return(capabilitiesStream); }