| # API |
| |
| [TOC] |
| |
| ## Example |
| |
| This example shows how to use **Get-Printer-Attributes** operation to get value |
| of **printer-make-and-model** from the printer: |
| |
| ```c++ |
| std::string url = "ipp://my.printer/ipp"; |
| std::vector<uint8_t> frame; // this is a buffer for IPP frames |
| |
| ipp::Frame request(ipp::Operation::Get_Printer_Attributes); |
| request.Groups(ipp::GroupTag::operation_attributes)[0].AddAttr("printer-uri", ipp::ValueTag::uri, url); |
| |
| frame = ipp::BuildBinaryFrame(request) |
| |
| // ... Now you have to send the content of frame as a payload in HTTP POST |
| // ... request with content-type set to application/ipp. |
| // ... Then you have to retrieve a payload from obtained HTTP response and |
| // ... save it to the buffer frame. Remember to check for HTTP errors. |
| |
| ipp::SimpleParserLog log; |
| ipp::Frame response = ipp::Parse(frame.data(), frame.size(), log); |
| if (!log.CriticalErrors().empty()) { |
| // critical parsing error |
| return false; |
| } |
| auto coll = response.Groups(ipp::GroupTag::operation_attributes)[0]; |
| auto it = coll.GetAttr("printer-make-and-model"); |
| if (it == coll.end()) { |
| std::cout << "Not included in the response :(\n"; |
| } else { |
| std::string mam; |
| it->GetValue(&mam); |
| std::cout << mam << "\n"; |
| } |
| ``` |
| |
| In `ipp_test.cc` in tests `example1`-`example9` you can find more examples how |
| to build IPP frames. |
| |
| > NOTE: The following attributes from the group **operation-attributes** are set |
| > automatically: |
| > |
| > * **attributes-charset**: set to `"utf-8"` |
| > * **attributes-natural-language**: set to `"en-us"`. |
| |
| |
| > WARNING: If the method `SimpleParserLog::CriticalErrors()` returns a non-empty |
| > vector it means that the parser did not finish parsing because of an error |
| > in the input data. In this case, returned `Frame` object contains only parsed |
| > part of the frame; in the worst-case scenario the returned object may be |
| > empty. If the parser was able to parse the whole frame the method |
| > `SimpleParserLog::CriticalErrors()` returns an empty vector. Even then, the |
| > method `SimpleParserLog::Errors()` can return a non-empty vector with some |
| > errors. This is because the parser can recover from many local errors in IPP |
| > frames by skipping incorrect values or attributes. These non-critical errors |
| > are also logged to `ParserLog`, but they do not abort parsing. Unfortunately, |
| > there is significant amount of printers that return IPP frames with this kind |
| > of non-critical errors. |
| |
| ## Parser |
| |
| The parser function is declared in the header `parser.h`. |
| |
| ```c++ |
| // Parse the frame of `size` bytes saved in `buffer`. Errors detected by the |
| // parser are saved to `log`. The returned object is always valid. In the |
| // worst case scenario (nothing was parsed), the returned frame is empty |
| // and has all basic parameters set to zeroes like after Frame() constructor. |
| // In case of parsing errors, some groups or attributes from the input buffer |
| // may be omitted. You should examine the content of ParserLog to make sure |
| // that the whole input frame was parsed. |
| Frame Parse(const uint8_t* buffer, size_t size, ParserLog& log); |
| ``` |
| |
| The function takes as an input a buffer of `size` bytes with an IPP frame (this |
| is probably a payload of a HTTP request), tries to parse it and returns parsed |
| content as a `Frame` object. |
| Errors detected by the parser are saved to `log` which implements the following |
| interface: |
| |
| ```c++ |
| class ParserLog { |
| /* ... */ |
| ParserLog(const ParserLog&) = delete; |
| ParserLog& operator=(const ParserLog&) = delete; |
| /* ... */ |
| |
| // Reports an `error` when parsing an element pointed by `path`. |
| virtual void AddParserError(const AttrPath& path, ParserCode error) = 0; |
| |
| // Reports that parsing was finished successfully. Calling it means that the |
| // parser reached the marker of the end of the frame and the parsed structure |
| // is valid. It is be called even if some minor errors were reported earlier. |
| virtual void ReportSuccessfulEndOfParsing() = 0; |
| }; |
| ``` |
| |
| There is a predefined class `SimpleParserLog` that implements this interface and |
| allows to browse reported errors: |
| |
| ```c++ |
| class SimpleParserLog : public ParserLog { |
| /* ... */ |
| struct Entry { |
| AttrPath path; |
| ParserCode error; |
| /* ... */ |
| }; |
| explicit SimpleParserLog(size_t max_entries_count = 100); |
| /* ... */ |
| |
| // Returns all entries added by AddParserError() in the same order they were |
| // added. The log is truncated <=> the number of entries reached the value |
| // `max_entries_count` passed to the constructor. |
| const std::vector<Entry>& Entries() const; |
| |
| // Returns true <=> ReportSuccessfulEndOfParsing() was called. |
| bool ParserFinishSuccessfully() const; |
| ... |
| }; |
| ``` |
| |
| > WARNING: The log is truncated when the number of entries reach the threshold |
| > specified in the constructor. |
| |
| ## Builder |
| |
| The header `builder.h` contains the following functions: |
| |
| ```c++ |
| // Return the size of the binary representation of the frame in bytes. |
| size_t CalculateLengthOfBinaryFrame(const Frame& frame); |
| |
| // Save the binary representation of the frame to the given buffer. Use |
| // CalculateLengthOfBinaryFrame() function before calling this method to make |
| // sure that the given buffer is large enough. The method returns the number of |
| // bytes written to `buffer` or 0 when `buffer_length` is smaller than binary |
| // representation of the frame. |
| size_t BuildBinaryFrame(const Frame& frame, uint8_t* buffer, size_t buffer_length); |
| |
| // Return the binary representation of the frame as a vector. |
| std::vector<uint8_t> BuildBinaryFrame(const Frame& frame); |
| ``` |
| |
| Function `BuildBinaryFrame()` returns binary representation of the given frame |
| that is ready to be sent as a payload in a HTTP frame. |
| |
| ## Validator |
| |
| Validator checks if the values contained in the given `Frame` object meet the |
| criteria required by IPP standard. |
| All frames sent to other parties should pass the validator without producing any |
| errors to ensure full compatibility. |
| Errors detected by the validator in incoming frames may be logged for |
| informational purposes. |
| |
| To use the validator you have to include the header `validator.h`. |
| It contains the following declarations: |
| |
| ```c++ |
| // Validates all groups in the `frame`. All detected errors are saved in `log` |
| // in the order they occur in the original frame. The function returns true <=> |
| // no errors were detected. |
| // For string types only the basic features are validated, there is no UTF-8 |
| // parsing or type-specific parsing like URL or MIME types. |
| bool Validate(const Frame& frame, ErrorsLog& log); |
| ``` |
| |
| The `log` is the abstract interface: |
| |
| ```c++ |
| class ErrorsLog { |
| public: |
| ErrorsLog(const ErrorsLog&) = delete; |
| ErrorsLog& operator=(const ErrorsLog&) = delete; |
| /* ... */ |
| |
| // Reports an `error` for the attribute at `path`. The errors are reported in |
| // the same order as they occurred in the frame. Return false if do not want |
| // to get any more AddValidationError() calls. |
| virtual bool AddValidationError(const AttrPath& path, AttrError error) = 0; |
| }; |
| ``` |
| |
| A simple implementation of this interface is also provided: |
| |
| ```c++ |
| // Simple implementation of the ErrorsLog interface. It just saves the first |
| // `max_entries_count` (see the constructor) errors in the frame. |
| class SimpleLog : public ErrorsLog { |
| public: |
| struct Entry { |
| AttrPath path; |
| AttrError error; |
| }; |
| explicit SimpleLog(size_t max_entries_count = 100); |
| bool AddValidationError(const AttrPath& path, AttrError error) override; |
| const std::vector<Entry>& Entries() const; |
| }; |
| ``` |
| |
| ## Frame class |
| |
| The `Frame` class represents an IPP frame. |
| The instance of this class may be obtained by calling the `Parse` function |
| described above or by using one of the constructors: |
| |
| ```c++ |
| class Frame { |
| /* ... */ |
| |
| // Constructor. Create an empty frame with all basic parameters set to 0. |
| Frame(); |
| |
| // Constructor. Create a frame and set basic parameters for IPP request. |
| // If `set_localization_en_us` is true (default) the Group Operation |
| // Attributes is added to the frame with two attributes: |
| // * "attributes-charset"="utf-8" |
| // * "attributes-natural-language"="en-us" |
| // If `set_localization_en_us` equals false, you have to add the Group |
| // Operation Attributes with these two attributes by hand since they are |
| // required to be the first attributes in the frame (see section 4.1.4 from |
| // RFC 8011). |
| explicit Frame(Operation operation_id, |
| Version version_number = Version::_1_1, |
| int32_t request_id = 1, |
| bool set_localization_en_us = true); |
| |
| // Constructor. The same as the previous constructor but for IPP response. |
| // There is no differences between frames created with this and the previous |
| // constructor. IPP requests and IPP responses have the same structure. |
| // Values of operation_id and status_code are saved in the same variable, |
| // they are just casted to different enums with static_cast<>(). The parameter |
| // `set_localization_en_us_and_status_message` works in similar way as the |
| // parameter `set_localization_en_us` in the constructor above but also adds |
| // the attribute "status-message" to the Group Operation Attributes. The value |
| // of the attribute is set to a string representation of the `status_code`. |
| // The attribute "status-message" is defined in the section 4.1.6.2 from |
| // RFC 8011. |
| explicit Frame(Status status_code, |
| Version version_number = Version::_1_1, |
| int32_t request_id = 1, |
| bool set_localization_en_us_and_status_message = true); |
| |
| /* ... */ |
| }; |
| ``` |
| |
| ### Header |
| |
| Each IPP frame has three fields in the header: |
| |
| * **operation-id** (IPP request) or **status-code** (IPP response) of type |
| `int16_t`. The two enums below contain predefined values for these fields: |
| |
| ```c++ |
| enum class Operation : int16_t { |
| Print_Job = 0x0002, |
| Print_URI = 0x0003, |
| Validate_Job = 0x0004, |
| Create_Job = 0x0005, |
| Send_Document = 0x0006, |
| Send_URI = 0x0007, |
| Cancel_Job = 0x0008, |
| Get_Job_Attributes = 0x0009, |
| /* ... |
| See "operations-supported" in |
| https://www.pwg.org/ipp/ipp-registrations.xml for other possible values. |
| ... */ |
| CUPS_Get_Default = 0x4001, |
| CUPS_Get_Printers = 0x4002, |
| CUPS_Add_Modify_Printer = 0x4003, |
| CUPS_Delete_Printer = 0x4004, |
| CUPS_Get_Classes = 0x4005, |
| CUPS_Add_Modify_Class = 0x4006, |
| CUPS_Delete_Class = 0x4007, |
| CUPS_Accept_Jobs = 0x4008, |
| /* ... |
| See "CUPS IPP Operations" in https://www.cups.org/doc/spec-ipp.html for |
| other CUPS-specific values. |
| ... */ |
| }; |
| |
| enum class Status : int16_t { |
| successful_ok = 0x0000, |
| successful_ok_ignored_or_substituted_attributes = 0x0001, |
| successful_ok_conflicting_attributes = 0x0002, |
| successful_ok_ignored_subscriptions = 0x0003, |
| successful_ok_too_many_events = 0x0005, |
| successful_ok_events_complete = 0x0007, |
| client_error_bad_request = 0x0400, |
| client_error_forbidden = 0x0401, |
| /* ... |
| See "Status Codes" in |
| https://www.pwg.org/ipp/ipp-registrations.xml for other possible values. |
| ... */ |
| }; |
| ``` |
| |
| * **version-number** that contains the version of IPP protocol. |
| It is coded as two bytes. The following enum contains predefined values: |
| |
| ```c++ |
| enum class Version : uint16_t { |
| _1_0 = 0x0100, |
| _1_1 = 0x0101, |
| _2_0 = 0x0200, |
| _2_1 = 0x0201, |
| _2_2 = 0x0202 |
| }; |
| ``` |
| |
| * **request-id** - a field of type `int32_t` that should be used to match an |
| IPP response to a corresponding IPP request (which is really not necessary |
| for HTTP over TCP). This field is expected to have a positive value. |
| |
| These three fields can also be changed/read with the following methods: |
| |
| ```c++ |
| class Frame { |
| /* ... */ |
| |
| // Access IPP version number. |
| Version& VersionNumber(); |
| Version VersionNumber() const; |
| |
| // Access operation id or status code. OperationId() and StatusCode() refer to |
| // the same field, they just cast the same value to different enums. The field |
| // is interpreted as operation id in IPP request and as status code in IPP |
| // responses. |
| int16_t& OperationIdOrStatusCode(); |
| Operation OperationId() const; |
| Status StatusCode() const; |
| |
| // Access request id. |
| int32_t& RequestId(); |
| int32_t RequestId() const; |
| |
| /* ... */ |
| }; |
| ``` |
| |
| ### **attribute-group** and GroupTag |
| |
| The frame's header is followed by a sequence of **attribute-group**. |
| Each **attribute-group** is labeled by a one-byte tag that is represented in |
| *libipp* by the following enum: |
| |
| ```c++ |
| enum class GroupTag : uint8_t { |
| operation_attributes = 0x01, |
| job_attributes = 0x02, |
| printer_attributes = 0x04, |
| unsupported_attributes = 0x05, |
| subscription_attributes = 0x06, |
| event_notification_attributes = 0x07, |
| resource_attributes = 0x08, |
| document_attributes = 0x09, |
| system_attributes = 0x0a, |
| }; |
| |
| // The correct values of GroupTag are 0x01, 0x02, 0x04-0x0f. This function |
| // checks if given GroupTag is valid. |
| constexpr bool IsValid(GroupTag tag); |
| |
| // This array contains all valid GroupTag values and may be used in loops like |
| // for (GroupTag gt: kGroupTags) ... |
| constexpr std::array<GroupTag, 14> kGroupTags{ /* ... */ }; |
| ``` |
| |
| In the majority of cases a frame contains at most one **attribute-group** for |
| each `GroupTag` value. |
| However, the format of IPP frame allows for more than one group with the same |
| tag. |
| Some of the IPP operations use this feature and requires responses containing |
| a sequence of many **attribute-group** with the same `GroupTag` |
| (e.g.: [**Get-Jobs**](https://www.rfc-editor.org/rfc/rfc8011#section-4.2.6.2)). |
| |
| ### Accessing **attribute-group** |
| |
| The following methods can be used to browse **attribute-group** in the `Frame` |
| object: |
| |
| ```c++ |
| class Frame { |
| /* ... */ |
| |
| // Provides access to groups with the given GroupTag. You can iterate over |
| // these groups in the following way: |
| // for (Collection& coll: frame.Groups(GroupTag::job_attributes)) { |
| // ... |
| // } |
| // or |
| // for (size_t i = 0; i < frame.Groups(GroupTag::job_attributes).size(); ) { |
| // Collection& coll = frame.Groups(GroupTag::job_attributes)[i++]; |
| // ... |
| // } |
| // The groups are in the same order as they occur in the frame. |
| CollsView Groups(GroupTag tag); |
| ConstCollsView Groups(GroupTag tag) const; |
| |
| /* ... */ |
| }; |
| |
| class CollsView { |
| /* ... */ |
| |
| CollsView(const CollsView& cv); |
| iterator begin(); |
| iterator end(); |
| const_iterator cbegin() const; |
| const_iterator cend() const; |
| const_iterator begin() const; |
| const_iterator end() const; |
| size_t size() const; |
| bool empty() const; |
| Collection& operator[](size_t index); |
| const Collection& operator[](size_t index) const; |
| |
| /* ... */ |
| }; |
| |
| class ConstCollsView { |
| /* ... */ |
| |
| ConstCollsView(const ConstCollsView& cv); |
| explicit ConstCollsView(const CollsView& cv); |
| const_iterator cbegin() const; |
| const_iterator cend() const; |
| const_iterator begin() const; |
| const_iterator end() const; |
| size_t size() const; |
| bool empty() const; |
| const Collection& operator[](size_t index) const; |
| |
| /* ... */ |
| }; |
| |
| ``` |
| |
| `iterator` and `const_iterator` are bidirectional iterators dereferenceable to |
| `Collection&` and `const Collection&`, respectively. |
| Class `Collection` represents a dictionary with attributes and is described in |
| the next section. |
| |
| ### Adding new **attribute-group** |
| |
| A new **attribute-group** can be added to the frame with the following method: |
| |
| ```c++ |
| class Frame { |
| /* ... */ |
| |
| // Add a new group of attributes to the frame. The iterator to the new group |
| // is saved in `new_group`. The returned iterator is valid in the range |
| // returned by Groups(tag). |
| // The returned value is one of the following: |
| // * Code::kOK |
| // * Code::kInvalidGroupTag |
| // * Code::kTooManyGroups |
| // If the returned value != Code::kOK then `new_group` is not modified. |
| Code AddGroup(GroupTag tag, CollsView::iterator& new_group); |
| |
| /* ... */ |
| }; |
| ``` |
| |
| ## Collection class |
| |
| Instances of this class are containers for objects of `Attribute` class that is |
| described in the next section. |
| Attributes belonging to the same `Collection` must have unique names. |
| |
| ### Accessing the attributes |
| |
| Attributes inside the `Collection` can be accessed with the following methods: |
| |
| ```c++ |
| // To iterate over all attributes in the collection use iterators, e.g.: |
| // |
| // for (Attribute& attr: collection) { ... } |
| // for (const Attribute& attr: collection) { ... } |
| // |
| // Attributes inside the collection are always in the same order they were added |
| // to it. They will also appear in the same order in the resultant frame. |
| class Collection { |
| public: |
| class const_iterator; |
| class iterator |
| /* ... */ |
| |
| // Standard container methods. |
| iterator begin(); |
| iterator end(); |
| const_iterator cbegin() const; |
| const_iterator cend() const; |
| const_iterator begin() const; |
| const_iterator end() const; |
| size_t size() const; |
| bool empty() const; |
| |
| // Methods return attribute by name. Methods return an iterator end() <=> the |
| // collection has no attributes with this name. |
| iterator GetAttr(std::string_view name); |
| const_iterator GetAttr(std::string_view name) const; |
| |
| /* ... */ |
| }; |
| ``` |
| |
| `iterator` and `const_iterator` are bidirectional iterators dereferenceable to |
| `Attribute&` and `const Attribute&`, respectively. |
| |
| ### Adding new attributes |
| |
| New attributes can be added to the `Collection` object with the following methods: |
| |
| ```c++ |
| class Collection { |
| /* ... */ |
| |
| // Add a new attribute without values. `tag` must be Out-Of-Band (see ValueTag |
| // definition). Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kInvalidValueTag |
| // * kIncompatibleType (`tag` is not Out-Of-Band) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, ValueTag tag); |
| |
| // Add a new attribute with one or more values. `tag` must be compatible with |
| // type of the parameter `value`/`values` according to the following rules: |
| // * int32_t: IsInteger(tag) == true |
| // * std::string: IsString(tag) == true OR tag == octetString |
| // * StringWithLanguage: tag == nameWithLanguage OR tag == textWithLanguage |
| // * DateTime: tag == dateTime |
| // * Resolution: tag == resolution |
| // * RangeOfInteger: tag == rangeOfInteger |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kInvalidValueTag |
| // * kIncompatibleType (see the rules above) |
| // * kValueOutOfRange (the vector is empty or one of the value is invalid) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, ValueTag tag, int32_t value); |
| Code AddAttr(const std::string& name, ValueTag tag, const std::string& value); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const StringWithLanguage& value); |
| Code AddAttr(const std::string& name, ValueTag tag, DateTime value); |
| Code AddAttr(const std::string& name, ValueTag tag, Resolution value); |
| Code AddAttr(const std::string& name, ValueTag tag, RangeOfInteger value); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<int32_t>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<std::string>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<StringWithLanguage>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<DateTime>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<Resolution>& values); |
| Code AddAttr(const std::string& name, |
| ValueTag tag, |
| const std::vector<RangeOfInteger>& values); |
| |
| // Add a new attribute with one or more values. Tag of the new attribute is |
| // deduced from the type of the parameter `value`/`values`. |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kValueOutOfRange (the vector is empty) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, bool value); |
| Code AddAttr(const std::string& name, int32_t value); |
| Code AddAttr(const std::string& name, DateTime value); |
| Code AddAttr(const std::string& name, Resolution value); |
| Code AddAttr(const std::string& name, RangeOfInteger value); |
| Code AddAttr(const std::string& name, const std::vector<bool>& values); |
| Code AddAttr(const std::string& name, const std::vector<int32_t>& values); |
| Code AddAttr(const std::string& name, const std::vector<DateTime>& values); |
| Code AddAttr(const std::string& name, const std::vector<Resolution>& values); |
| Code AddAttr(const std::string& name, |
| const std::vector<RangeOfInteger>& values); |
| |
| // Add a new attribute with one or more collections. The first method creates |
| // an attribute with a single collection and returns an iterator to it in the |
| // last parameter. The second method creates an attribute with `size` |
| // collections and returns a view of them in the last parameter. |
| // Possible errors: |
| // * kInvalidName |
| // * kNameConflict |
| // * kValueOutOfRange (`size` is out of range) |
| // * kTooManyAttributes. |
| Code AddAttr(const std::string& name, CollsView::iterator& coll); |
| Code AddAttr(const std::string& name, size_t size, CollsView& colls); |
| |
| /* ... */ |
| }; |
| ``` |
| |
| ## Attribute class |
| |
| Instances of this class can store values defined in IPP specification. |
| |
| ### Tag and name |
| |
| The type of stored values is defined by one-byte **value-tag** represented by |
| the following enum: |
| |
| ```c++ |
| // ValueTag defines type of an attribute. It is also called as `syntax` in the |
| // IPP specification. All valid tags are listed below. Values of attributes with |
| // these tags are mapped to C++ types. |
| enum class ValueTag : uint8_t { |
| // 0x00-0x0f are invalid. |
| // 0x10-0x1f are Out-of-Band tags. Attributes with this tag have no values. |
| // All tags from the range 0x10-0x1f are valid. |
| unsupported = 0x10, // [rfc8010] |
| unknown = 0x12, // [rfc8010] |
| no_value = 0x13, // [rfc8010] |
| not_settable = 0x15, // [rfc3380] |
| delete_attribute = 0x16, // [rfc3380] |
| admin_define = 0x17, // [rfc3380] |
| // 0x20-0x2f represents integer types. |
| // Only the following tags are valid. They map to int32_t. |
| integer = 0x21, |
| boolean = 0x22, // maps to both int32_t and bool. |
| enum_ = 0x23, |
| // 0x30-0x3f are called "octetString types". They map to dedicated types. |
| // Only the following tags are valid. |
| octetString = 0x30, // maps to std::string |
| dateTime = 0x31, // maps to DateTime |
| resolution = 0x32, // maps to Resolution |
| rangeOfInteger = 0x33, // maps to RangeOfInteger |
| collection = 0x34, // = begCollection tag [rfc8010], maps to Collection |
| textWithLanguage = 0x35, // maps to StringWithLanguage |
| nameWithLanguage = 0x36, // maps to StringWithLanguage |
| // 0x40-0x5f represents 'character-string values'. They map to std::string. |
| // All tags from the ranges 0x40-0x49 and 0x4b-0x5f are valid. |
| textWithoutLanguage = 0x41, |
| nameWithoutLanguage = 0x42, |
| keyword = 0x44, |
| uri = 0x45, |
| uriScheme = 0x46, |
| charset = 0x47, |
| naturalLanguage = 0x48, |
| mimeMediaType = 0x49 |
| // memberAttrName = 0x4a is invalid. |
| // 0x60-0xff are invalid. |
| }; |
| |
| // Is valid Out-of-Band tag (0x10-0x1f). |
| constexpr bool IsOutOfBand(ValueTag tag); |
| |
| // Is valid integer type (0x21-0x23). |
| constexpr bool IsInteger(ValueTag tag); |
| |
| // Is valid character-string type (0x40-0x5f without 0x4a). |
| constexpr bool IsString(ValueTag tag); |
| |
| // Is valid tag. |
| constexpr bool IsValid(ValueTag tag); |
| ``` |
| |
| Each instance of the `Attribute` class has assigned exactly one tag (`ValueTag`) |
| and a name (`std::string`). |
| They are permanent through the whole lifetime of the object and cannot be |
| changed. |
| The tag and the name of the attribute can be checked with the following API: |
| |
| ```c++ |
| class Attribute { |
| /* ... */ |
| |
| // Returns tag of the attribute. |
| ValueTag Tag() const; |
| |
| // Returns an attribute's name. It is always a non-empty string. |
| std::string_view Name() const; |
| |
| /* ... */ |
| }; |
| ``` |
| |
| ### Values |
| |
| Attributes have also a vector of values whose type is determined by the |
| attribute's `ValueTag`. |
| Attributes can be divided into three groups by type: |
| |
| * **out-of-band** (`IsOutOfBand(tag) == true`) - these attributes |
| cannot have any values, they have only name and tag. |
| * standard attributes (`IsOutOfBand(tag) == false && tag != collection`) - |
| these attributes always have assigned a non-empty vector of values. |
| *libipp* provides dedicated types for some specific tags: |
| |
| ```c++ |
| // It is used to hold name and text values (see [rfc8010]). |
| // If language == "" it represents nameWithoutLanguage or textWithoutLanguage, |
| // otherwise it represents nameWithLanguage or textWithLanguage. |
| struct StringWithLanguage { |
| std::string value = ""; |
| std::string language = ""; |
| }; |
| |
| // Represents resolution type from [rfc8010]. |
| struct Resolution { |
| int32_t xres = 0; |
| int32_t yres = 0; |
| enum Units : int8_t { |
| kDotsPerInch = 3, |
| kDotsPerCentimeter = 4 |
| } units = kDotsPerInch; |
| }; |
| |
| // Represents rangeOfInteger type from [rfc8010]. |
| struct RangeOfInteger { |
| int32_t min_value = 0; |
| int32_t max_value = 0; |
| }; |
| |
| // Represents dateTime type from [rfc8010,rfc2579]. |
| struct DateTime { |
| uint16_t year = 2000; |
| uint8_t month = 1; // 1..12 |
| uint8_t day = 1; // 1..31 |
| uint8_t hour = 0; // 0..23 |
| uint8_t minutes = 0; // 0..59 |
| uint8_t seconds = 0; // 0..60 (60 - leap second :-) |
| uint8_t deci_seconds = 0; // 0..9 |
| uint8_t UTC_direction = '+'; // '+' / '-' |
| uint8_t UTC_hours = 0; // 0..13 |
| uint8_t UTC_minutes = 0; // 0..59 |
| }; |
| ``` |
| |
| * attributes with collections (`tag == collection`) - these attributes |
| contains a non-empty vector of `Collection` objects (with |
| sub-attributes). |
| |
| The `Attribute` class has the following interface allowing to get and set the |
| number of containing values: |
| |
| ```c++ |
| class Attribute { |
| /* ... */ |
| |
| // Returns the current number of elements (values or Collections). |
| // It returns 0 <=> IsOutOfBand(Tag()). |
| size_t Size() const; |
| |
| // Resizes the attribute (changes the number of stored values/collections). |
| // When (IsOutOfBand(Tag()) or `new_size` equals 0 this method does nothing. |
| void Resize(size_t new_size); |
| |
| /* ... */ |
| }; |
| ``` |
| Values of standard attributes (`!IsOutOfBand(Tag()) && Tag() != collection`) can |
| be retrieved and set with the following methods: |
| |
| ```c++ |
| // Retrieves a single value from the attribute and saves it in `value`. |
| // Possible errors: |
| // * kIncompatibleType |
| // * kIndexOutOfRange |
| // The second parameter must match the type of the attribute, otherwise the |
| // code kIncompatibleType is returned. However, there are a couple of |
| // exceptions when the underlying value is silently converted to the type of |
| // the given parameter. See the table below for a list of silent conversions: |
| // |
| // ValueTag | target C++ type |
| // -------------------+-------------------- |
| // boolean | int32_t (0 or 1) |
| // enum | int32_t |
| // integer | RangeOfIntegers (as range [int,int]) |
| // nameWithoutLanguage | StringWithLanguage (language is empty) |
| // textWithoutLanguage | StringWithLanguage (language is empty) |
| // |
| Code GetValue(size_t index, bool& value) const; |
| Code GetValue(size_t index, int32_t& value) const; |
| Code GetValue(size_t index, std::string& value) const; |
| Code GetValue(size_t index, StringWithLanguage& value) const; |
| Code GetValue(size_t index, DateTime& value) const; |
| Code GetValue(size_t index, Resolution& value) const; |
| Code GetValue(size_t index, RangeOfInteger& value) const; |
| |
| // Retrieves values from the attribute. They are copied to the given vector. |
| // Possible errors: |
| // * kIncompatibleType |
| // These methods follow the same rules for types' conversions as GetValue(). |
| Code GetValues(std::vector<bool>& values) const; |
| Code GetValues(std::vector<int32_t>& values) const; |
| Code GetValues(std::vector<std::string>& values) const; |
| Code GetValues(std::vector<StringWithLanguage>& values) const; |
| Code GetValues(std::vector<DateTime>& values) const; |
| Code GetValues(std::vector<Resolution>& values) const; |
| Code GetValues(std::vector<RangeOfInteger>& values) const; |
| |
| // Set new values for the attribute. Previous values are discarded. |
| // Possible errors: |
| // * kIncompatibleType |
| // * kValueOutOfRange |
| Code SetValues(bool value); |
| Code SetValues(int32_t value); |
| Code SetValues(const std::string& value); |
| Code SetValues(const StringWithLanguage& value); |
| Code SetValues(DateTime value); |
| Code SetValues(Resolution value); |
| Code SetValues(RangeOfInteger value); |
| Code SetValues(const std::vector<bool>& values); |
| Code SetValues(const std::vector<int32_t>& values); |
| Code SetValues(const std::vector<std::string>& values); |
| Code SetValues(const std::vector<StringWithLanguage>& values); |
| Code SetValues(const std::vector<DateTime>& values); |
| Code SetValues(const std::vector<Resolution>& values); |
| Code SetValues(const std::vector<RangeOfInteger>& values); |
| ``` |
| |
| Attributes with collections can be browsed by using the method `Colls()` |
| returning a view of a vector of `Collection`s. |
| |
| ```c++ |
| // Provides access to Collection objects. You can iterate over them in the |
| // following ways: |
| // for (Collection& coll: attr.Colls()) { |
| // ... |
| // } |
| // or |
| // for (size_t i = 0; i < attr.Colls().size(); ) { |
| // Collection& coll = attr.Colls()[i++]; |
| // ... |
| // } |
| // (Tag() != collection) <=> attr.Colls().empty() |
| CollsView Colls(); |
| ConstCollsView Colls() const; |
| ``` |