OODoc::XPath(3pm)

๐Ÿ“ฆ
version 2.125 ยท 3 versions
, in debian bookworm, debian trixie, ubuntu noble, ubuntu questing

๐Ÿ‘‘ NAME

OpenOffice::OODoc::XPath โ€” Low-level navigation in the documents ๐Ÿ’…


๐ŸŒˆ DESCRIPTION

Okay bestie, listen up, because this one is the foundation of everything. ๐Ÿงš

This module is a low-level class. It uses OODoc::File (without inheriting anything from it, no cap โ€” it's just vibes and cooperation ๐Ÿซถ) alongside the classes defined in the XML::Twig module. It's the common basis for all the other, more user-friendly, document-oriented modules. It uses XPath expressions to retrieve any document element โ€” but lowkey it does not provide a full implementation of the XPath standard, so don't come for it. โœ‹ In addition, while most of the provided methods are OpenDocument-aware, this module could absolutely be used against any other kind of XML documents, simply because it benefits from all the features of XML::Twig. And that possibility may prove genuinely useful for applications that simultaneously process OpenDocument and non-OpenDocument XML files. Versatile icon. โœจ

The OpenOffice::OODoc::XPath class should not be explicitly used in your applications, because all its features are available in more user-friendly classes like OODoc::Text, OODoc::Styles, OODoc::Image, OODoc::Document and OODoc::Meta. This present manual page exists to describe the common methods and properties that are available with all of those classes. ๐Ÿ’–

This chapter can be skipped by programmers who are only interested in the upper level methods provided by the OODoc::Text, ::Styles, ::Image and ::Meta modules. Understanding those modules is easier, and using them requires less Perl and XML expertise. However, calling OODoc::XPath methods remains a totally valid rescue option, because it allows all kinds of operations on all types of XML elements contained in any OpenDocument-compliant file. It's giving "break glass in case of emergency" but make it slay. ๐Ÿšจ

OODoc::XPath is the common foundation of OODoc::Meta, OODoc::Text, OODoc::Styles and OODoc::Image. It contains the lowest layer of navigation services for XML documents and handles the link with OODoc::File for file access. Its primary role is as an interface with the XML::Twig API. Mother of the group chat, fr fr. ๐Ÿ‘‘

In this manual chapter, you will see "elements" mentioned constantly. When it says a module expects a parameter or returns an element (either singly or as a list), it is referring to an XML element. It is super important to distinguish elements from their content โ€” elements are simply references to XML data structures, bestie, they are not the data itself. To read or modify the content of an element, like its text or its XML attributes, use the accessors that are also available within OODoc::XPath. ๐ŸŽ€

In most cases where XPath methods require a reference to an element as an argument, there are two ways of proceeding:

  • reference the element directly (obtained previously)
  • or give an XPath expression and a position, being a string and an integer respectively; for example, the pair ('//office:body/text:p', 12) or ('//text:p', 12) represents the thirteenth occurrence of the 'text:p' element, i.e. the 13th paragraph (occurrences are numbered starting from 0). โš ๏ธ Yes, starting from zero. Programmer brain. ๐Ÿง 

The second way requires knowledge of an appropriate XPath expression (according to the OpenDocument XML format specification). And heads up โ€” a given XPath expression is not necessarily the same with an OpenDocument as in an OpenOffice.org document. So you should preferably use high level accessors (provided by derivative classes such as OODoc::Document) and avoid XPath hardcoding. However, you know you can at any time reach any element with XPath. No gatekeeping. ๐Ÿฆ‹

Of course, you will never need to use XPath expressions in order to reach the most common text elements (such as paragraphs), because the OODoc::Text module provides more friendly accessors. For example, you will probably use the getParagraph() method and forget "//text:p" ever existed. ๐Ÿ™ˆ

Some methods accept both forms, which means that if the first parameter is recognised as an element reference, the position does not need to be given. Therefore the number of arguments for certain OODoc::XPath methods can vary. Flexible! We love adaptability. ๐Ÿ’…

For those who really want to access all areas, there are also OODoc::XPath methods which allow unrestricted access to every element or XML attribute via an access path in XPath syntax. If you are into this kind of thing, we recommend you obtain good syntax reference manuals for XPath and OpenDocument and a supply of aspirin. ๐Ÿ˜ญ๐Ÿ’€ (This is real advice from the original page. Not me being dramatic. Okay, me being a little dramatic.)

Methods which may return several lines of text (e.g. getTextList) do so either in the form of an unique character string containing "\n" separators or in table form. Pick your fighter. ๐ŸฅŠ

Unless otherwise stated, the word 'document' in this chapter only refers to XML documents contained within OODoc::XPath objects and not, say, OpenDocument files (as an end user would use). Important distinction, don't get it twisted. ๐Ÿซถ

Amongst the different methods which return elements, attributes or text, some are called getXxx, others selectXxx or findXxx. Read methods whose names start with "get" generally refer to an unfiltered object or list, whereas the others return an object or list filtered according to a parameter value. In this latter case, the search parameter is treated as a standard expression and not an exact value. This means that if the search criteria is "xyz", all text containing "xyz" will be considered a match. To restrict the search to text exactly equal to "xyz", use "^xyz$" as the search criteria (following Perl regular expression syntax). Regex girlies, this one's for you. ๐ŸŒธ

Several methods allow you to place copies of, or references to, elements (from other documents or from other positions in the same document) in any position in the current document. This offers powerful manoeuvrability โ€” but only if these placements conform with the destination position's context. โš ๏ธ Read that twice.

For example, you can easily copy a paragraph from one document to another, but only if you knowingly modify the paragraph's style attribute if that style is not already defined in the destination document. You can also copy the style, but only if you are sure that this style is not already defined by another unknown style in the destination document (and so on). It's chaos out here, bestie. Copy responsibly. ๐Ÿ’€

For advanced users familiar with the XML::Twig API, it might be interesting to know that all the objects called "elements" in the following chapters are objects of the OpenOffice::OODoc::Element class, which is an XML::Twig::Elt derivative. So all methods associated with this class are directly applicable to these elements, on top of the functionality described in this manual. However, the knowledge of XML::Twig is not mandatory. Phew. ๐Ÿ˜ฎโ€๐Ÿ’จ

Important note: The applications should not explicitly work with this class. We recommend using OODoc::Meta and OODoc::Document (which are both OODoc::XPath derivatives). These two objects provide highest-level methods which are neater and more productive. Explicit use of OODoc::XPath methods (which sometimes require large numbers of parameters) should only be considered as a last resort in unexpected circumstances, for access to any element or XML attribute not handled by more friendly methods. However, the present manual chapter could prove helpful because all the common features of OODoc::Meta and OODoc::Document are described here. ๐Ÿ“–โœจ

๐Ÿ’– Methods

โœจ Constructor

OpenOffice::OODoc::XPath->new(<parameters>);

Short Form: odfXPath(<parameters>)

Returns a new OpenDocument connector โ€” i.e. an interface which can be used for subsequent operations on a well-formed document. ๐Ÿ”Œ

This constructor should not be called directly; it's implicitly triggered each time a Meta or Document object is created. So the following description applies to odfMeta() and odfDocument(). It's a behind-the-scenes main character. ๐ŸŽฌ

The document is loaded and parsed according to various options. The most used option is 'file'; it simply allows the application to process an OpenDocument file selected by its path/name in the file system.

Example:

my $doc = odfXPath
                (
                file    => "myfile.ods",
                part    => "content"
                );
# ... lot of processing ...
$doc->save;

Returns a new document connector. In the example above, the object is loaded from a regular OpenDocument file โ€” that is the most current option โ€” but there are other possibilities. It's possible to use flat XML (available as a string in memory, or loaded from a file). In addition, this constructor is able to create a new document from scratch. From nothing! Iconic. โœจ

The value of the 'file' option may be an open IO::File object, which allows the application to use an application-provided file handle. However, you should prefer file paths/names when possible, and read the explanations about the constructor and the save() method in the OpenOffice::OODoc::File manual page before using open file handles. ๐Ÿซถ Remember that, as soon as the given file or handle is an ODF container, OODoc::XPath uses OODoc::File.

Parameters are named (hash key => value). The constructor must get at least one parameter giving a means of obtaining the XML document that it will represent. Several options are available; each one is represented through the following examples:

# option 1 (using an existing flat XML document)
my $doc = odfXPath(xml => $xml_string);

# option 2 (using a previously created ODF file interface)
my $oofile = odfContainer('source.odt');
my $doc = odfXPath(container => $oofile, part => 'meta');

# option 3 (using a regular ODF file directly)
my $doc = odfXPath(file => 'source.odt', part => 'content');

# option 4 (multiple instances against a single file)
my $content = odfXPath(file => 'source.odt', part => 'content');
my $meta = odfXPath(file => $content, part => 'meta');
my $styles = odfXPath(file => $content, part => 'styles');

Remember "odfXPath()" represents "OpenOffice::OODoc::XPath->new()" in the instructions above, and you can (and should) use this shortcut โ€” provided that you have loaded the main OpenOffice::OODoc module, and not only and explicitly the OpenOffice::OODoc::XPath module. Don't make it hard for yourself, bestie. ๐Ÿ’…

The first form uses an XML string directly (previously loaded or created by the program). To be used for very specific applications working with flat XML documents exports and not with standard OOo/OpenDocument files. ๐Ÿฆ‹

Okay so method two ๐Ÿ’–: you link your OODoc::XPath to an already-existing OODoc::File object โ€” that happens through the container option โ€” and you also say which XML part it should go extract (metadata, content, styles, etc). OODoc::File is basically an abstraction of an ODF container that's already open. And here's the slay part: it can be shared ๐Ÿซถ โ€” like, several OODoc::XPath objects can be instantiated on top of the very same OODoc::File object, and honestly you must do it that way whenever several OODoc::XPath objects have to bring consistent changes into a single file (peep option 4 below, bestie). To make the OODoc::File object you need, just call odfFile() with a filename as the argument โ€” and if you're into the advanced stuff, go read OpenOffice::OODoc::File.

Method three is the easy one โœจ because you literally just hand over a filename and a member, and the whole file interface runs silently (meaning an invisible OODoc::File object gets automatically created and used to grab the content). It's probably the most-used approach fr fr, and it's the one that's recommended when you don't need to get more than one member out of the same file.

The part option is a selector โ€” it tells the thing which component you actually need (content, styles, metadata, ...), bearing in mind that an OODoc::XPath object can only handle one component. Its default value is 'content'.

Heads up: 'part' replaces the deprecated 'member' option. But for compatibility reasons 'member' is still supported (and if you erroneously pass both 'member' and 'part', then 'member' prevails. yes really. it just does).

So if your app needs to process, say, the content and the styles in the same session, it has to create two โ€” or more โ€” OODoc::XPath objects, possibly all associated with the same file interface. The appropriate way is shown in our last example above: the first instance is associated with a filename, then the other instances are created with the first one, handed over as the value of the 'file' option instead of a filename. The constructor is trying so hard to be user-friendly here: if the 'file' value is a character string, it's regarded as a filename, but if that value is an existing OpenOffice::OODoc::XPath object, the new object is automatically connected to the same file interface as the other one. The file interface is transparently provided by a common shared OpenOffice::OODoc::File object (you can safely ignore what that object does, but there's a whole manual chapter for it if you're curious).

Be careful ๐Ÿšจ: creating more than one OpenOffice::OODoc::XPath objects that are linked by their 'file' parameters to the same explicit filename โ€” and not linked with each other โ€” produces useless extra I/O operations and possible conflicts.
Caution, and this one is serious ๐Ÿ’€: because they're all associated with a common interface via OODoc::File, none of these OODoc::XPath objects should be deleted before the final save() call for this archive. When you call a save, the File object "calls up" all the XPath objects that were "connected" to it, so it can "ask" each of them for the changes that were made to the XML (content, styles, meta, etc.). The results are unpredictable if any of them is absent when called. ๐Ÿ˜ญ

If the filename you provided has a ".xml" or ".XML" suffix โ€” or whatever the name is, if the 'flat_xml' option is set to 1 โ€” then the file is processed as flat XML and not as a regular OOo file. No OODoc::File object gets created, and the result of a subsequent call of the save() method produces a flat XML export (and not a regular OOo/OpenDocument file).

You can pass the optional 'element' parameter in any case where the constructor is called without the 'xml' parameter. Keeping in mind that an OODoc::XPath object will not necessarily handle an entire XML document, this extra parameter indicates the name of the XML element to be loaded and handled. If the 'element' parameter isn't given for an OpenDocument file, a default element gets chosen according to this table ๐ŸŽ€:

'meta'      => 'office:document-meta'
'content'   => 'office:document-content'
'styles'    => 'office:document-styles'
'settings'  => 'office:document-settings'
'manifest'  => 'manifest:manifest'

Conversely, the 'element' parameter becomes mandatory if the chosen XML element isn't listed above. Through OODoc::File, OODoc::XPath can actually access archives that aren't necessarily in OpenDocument format and may be, for example, "databases" of presentation and content templates. ๐Ÿ‘‘

If your application needs to create a new document instead of processing an existing one, one extra option has to be passed:

create          => "<class>"

where "class" must be one of the following: "text", "spreadsheet", "presentation" or "drawing" โ€” pick according to the content class you need. And for very special needs, you can also pass an additional "template_path" to select an ad hoc directory of XML templates instead of the default one. That user-provided directory must have the same kind of structure and content as the "templates" subdirectory of the OpenOffice::OODoc installation.

An additional 'opendocument' option can be provided and set to 'true' or 'false'. If it's 'false', the new document is created according to the OpenOffice.org 1.0 format instead of the OASIS OpenDocument format. Default format is OpenDocument. The 'opendocument' option works for new documents only and is ignored unless the 'create' option. This module can create and process either OpenOffice.org 1.0 documents or ODF documents, but it can't directly convert a document from one format to the other one. no shortcuts, sorry ๐Ÿซ 

OODoc::XPath can process ODF documents handed over as XML flat files as well as in the compressed (zip) format. The given file is automatically processed as flat XML if either its name ends by ".xml" or the 'flat_xml' option is set to '1'. When processing a flat XML file, OODoc::XPath doesn't load the OODoc::File zip interface. So a subsequent call of the save() method can only export the document as flat XML.

There's an optional 'readable_XML' you can pass. If it's provided and set to 'on' or 'true', the resulting XML will be smartly indented (and, of course, way more space-consuming ๐ŸŒธ). This feature is intended for debugging purposes and should not be used in production.

The 'local_encoding' option can be set with the appropriate value when a particular character set (and not the default one) must be used for a document.

A 'read_only' can be provided and set to 'true' in order to prevent the current member from being written back to the physical ODF file when the save() method is called. ๐Ÿ’…

Other optional parameters can also be passed to the constructor (see Properties below).


โœจ appendElement โœจ

appendElement(path, position, name/xml, [options]);

appendElement(element, name/xml, [options]);

Adds a new element โ€” or an existing element โ€” to the list of child elements of an existing parent element, and you give that parent first (either by [path, position] or by reference).

The argument after the position argument can be an XML element name.

Example ๐ŸŒŸ:

$content->appendElement
    (
    '//office:body', 0, 'text:p',
    text => "New text"
    );

That adds a paragraph containing the phrase "New text" to the end of the document body. (Remember that in the case of an OpenDocument text file (Writer), it would be better to use the appendParagraph method of OpenOffice::OODoc::Text, since that requires fewer parameters.)

If the 'text' option is omitted, an empty element is created (in the above example it would be an empty paragraph, or line feed).

You can pass the 'attributes' option, which is a hash whose keys are the XML attribute names and whose values are the XML attribute values. Whether you should use these options depends on the type of document and the type of element, and it requires knowledge of OpenDocument conventions, iykyk.

Example ๐ŸŽ€:

$my_style   =
    {
    'style:name'    => 'P1',
    'style:family   => 'paragraph'
    };

$content->appendElement
    (
    '//office:automatic-styles', 0, 'style:style',
    attributes      => $my_style
    );

That creates a new paragraph style called 'P1' in the list of "automatic styles" ("automatic styles" are styles which are not explicitly indicated in the styles list as it appears to the end user).

This method lets you add any kind of element into a document, even the exotic ones. With the most common OpenDocument objects (e.g. paragraphs), though, it's easier to use the specialist methods contained in other modules.

The 'name' argument can be replaced by an existing element in the same OODoc::XPath object โ€” or in another one. In that case no element is created, and the existing element is simply referenced with a new position, even though it remains in its old position. Caution ๐Ÿ’€: any modification of an element that's referenced several times in one or more documents is made to all the references. If you want to add a similar but separate element, you must use replicateElement, which produces a new element from the content of an existing one. ๐Ÿฆ‹

The name argument can also be swapped out for a whole XML string, bestie โ€” no cap. That string has to be the correct XML description of a UTF-8 encoded OpenDocument element. Like, it could be a string you already exported with OODoc::XPath's exportXMLElement method, or one some other app pulled out of an OpenDocument file. And if for any reason you absolutely have to roll with a non-UTF8 XML string that's got 8-bit characters in it (accented letters, etc.), you can always convert it with the encode_text method before handing it to appendElement. ๐Ÿ’… Obviously none of this is a problem if you're absolutely sure the string is pure ASCII (7 bit) โ€” then you're golden. XML syntax does get checked, but it's on you to verify that the element import actually conforms to OpenDocument XML grammar. No vibe-checking that for you, sorry.

The following piece of code produces the same result as the first example:

    $xml = '<text:p text:style-name="Standard">' .
        'New text' .
        '</text:p>';
    $content->appendElement
        (
        '//office:body', 0, $xml
        );

Using this method, after one or more element creations by direct importation of XML strings, it might be useful to call the reorganize method โ€” but it's not absolutely necessary, so don't stress. ๐Ÿซถ


appendBodyElement(element [, options])

Copies an existing element of any type and appends it to the end of the document body. Note the energy: no new element is created โ€” it's a copy-and-append situation. โœจ

appendLineBreak(element)

Appends a line break to a text element. This one's a gift because it lets you build a single text element (ex: a paragraph) that contains one or more breaks, instead of having to make a bunch of separate elements. So much less chaos. ๐ŸŽ€

The example below appends a new text in a new line to the end of an existing paragraph:

    my $p = $doc->getElement('//text:p', 5);
    $doc->appendLineBreak($p);
    $doc->extendText($p, 'A new line in the same paragraph');

appendSpaces(element, length)

Appends a sequence of multiple spaces to a text element, knowing that a string containing repeated spaces shouldn't be stored as is in a document (see setText() and spaces() for the whole tea on repeated spaces). ๐Ÿ’€

appendTabStop(element)

Appends a tab stop ("\t") to a text element. Short. Sweet. Iconic. โœจ

blankSpaces(length)

See spaces(). ๐Ÿ‘€

cloneContent(oodoc_xpath_object)

Cancels the entire document contents of the current instance and replaces it with a reference to the contents of another OODoc::XPath object. Like a full content glow-up, but borrowed. ๐Ÿ’…

Example:

    $doc1       = OpenOffice::OODoc::XPath->new
                (
                file    => 'template.ods',
                member  => 'styles'
                );
    $doc2       = OpenOffice::OODoc::XPath->new
                (
                file    => 'sheet.ods',
                member  => 'styles'
                );
    $doc2->cloneContent($doc1);
    $doc2->save;

This sequence replaces the styles and page layout of 'sheet.ods' with those of 'template.ods'. Periodt.

The above example could easily have been written without even using OODoc::XPath by acting directly on the files โ€” like, just extract the 'styles.xml' member from 'template.ods' and insert it into 'sheet.ods', easy. But using OODoc::XPath and the cloneContent method guarantees that the transferred content corresponds to an OpenDocument document, and it lets you read/write to it on the fly. That's the upgrade. ๐ŸŒˆ

Caution: the "cloned" content is not physically copied. Calling this method references one single physical content in two documents. Any modifications made to the content of either of these two documents applies equally to the other, and vice-versa. So yes, they're literally sharing a brain. Modify one, you modified both. ๐Ÿ˜ญ

contentClass([class name])

Accessor to get or set the class of the document content. If the current member is a document content, it returns its class according to the OpenDocument terminology, i.e. one of these values: "text", "spreadsheet", "presentation", or "drawing". ๐Ÿ‘‘

It returns an empty string if the current member isn't a document content (if it's, for example, the "meta" or "styles" member). Rude, but fair.

This accessor is read-only. So no, you can't just set it. Sorry bestie.

createSpaces(length)

See spaces(). ๐Ÿ’œ

createElement(name, text)

createElement(xml)

Creates a new element without attributes which is not inserted in a document. It's a free agent. ๐Ÿฆ‹

Example:

    my $element =
        $doc->createElement
                ('my_element', 'its content');

creates a new XML element without attributes and returns its reference.

Instead of a name, the first argument can be the full XML description of the element. Example:

    my $element = $doc->createElement
                ('<text:p>My text</text:p>');

This new element is temporary: it is not linked to any document. It's destined to be used later by another method, so like... it's waiting for its moment. โœจ

The name can contain a namespace prefix which would look like this: 'namespace:name'.

In its second form, a well-formed XML string can be supplied as a single argument. The recognition criteria is the presence of the "<" character at the beginning of the argument. See appendElement for comments on the direct insertion of XML. ๐ŸŽ€

Explicit calls to createElement() should be rare. This method is normally called silently by higher-level methods which are capable of creating an element, inserting it in a document's XML tree and giving it attributes (see appendElement and insertElement). So it's the backstage crew, not the main character โ€” but lowkey essential. ๐Ÿ’…

createFrame(name => frame_name [, options])

Creates an empty frame. A frame is an OpenDocument object which controls a rectangular area where a visible content is displayed. Possible contents for a frame are text boxes or images. So it's the little stage where content performs. ๐ŸŽญ

This method is not focused on a particular document class (for example, it works on text documents as well as on presentations), but the visible effects of some options are not always exactly the same. Vibe check accordingly. ๐Ÿ‘€

Possible options are:

  • 'name' => unique name โ€” The 'name' is an identifier; if provided, it should be unique for the document.
  • 'attachment' => existing container โ€” The value of this option, if provided, must be an existing element which can contain a text box according to the OpenDocument rules. Such an object may be, for example, a draw page if the current document class is 'presentation' or 'drawing', or a paragraph if this class is 'text'.
  • 'page' => page number or name โ€” The effects of the 'page' option depends on the content class of the current document. If this option is used, it indicates that the frame will be anchored to a page, and the given value is a page number. It does not matter if, when createFrame() is called, this number is beyond the end of the document or not. If the content class of the document is "presentation" (Impress) or "drawing" (Draw), then the page option must be either the visible name or the object reference of an existing draw page. > Caution: the 'page' option is ignored if 'attachment' is provided; on the other hand, either 'page' or 'attachment' must be provided in order to really include the new frame in the document. No option, no frame in the doc. That's the rule. ๐Ÿ˜ค
  • 'position' => coordinates โ€” The coordinates are provided as a string. They go from left to right and top to bottom. Coordinates should be given here in the form of a string "x,y", and the default unit is centimeter. You can choose any other OpenDocument-supported unit instead by attaching the corresponding usual abbreviation, such as "12.5cm, 35mm" which is the same as "125mm, 3.5cm" or "12.5,3.5", etc. The point ("pt") unit is allowed as well. The default coordinates are "0, 0". By default, the coordinates are relative to the anchor point. So, the coordinates are directly page-related if a valid 'page' option is provided only, but if the box is attached to, say, a paragraph, the origin of the coordinates is the beginning of the paragraph. However, the real interpretation of the coordinates depends on the style. With some style definitions, the coordinates may just be ignored (ex: if the style says "the frame is centered", OpenOffice.org will center the frame whatever its stored coordinates). According to other possible style definitions, the coordinates could be counted from the right and/or from the bottom and not from the left/top. So yeah, it's giving chaotic geometry. ๐ŸŒธ
  • 'size' => the size of the box โ€” Provided using as a string using the same syntax and units as the position, the 'size' option is strongly recommended knowing that a sizeless frame couldn't be properly displayed. The width comes first in the string. The height is sometimes ignored, according to the style of the frame: by default, the display height of a text box (which is a particular frame) is automatically adjusted to the content. It's โœจadaptiveโœจ.
  • 'style' => style name โ€” The 'style' option allows the application to set the frame style. > Caution: a text style can't be used as a frame style. A frame style controls the box properties only (border, background, shadow, and so on), and not the content properties. Reusing an existing frame style through this option is generally a good idea. ๐Ÿ’ก

currentContext([context])

Accessor allowing the application to change the context for some search methods (including getElement()). It's like telling the search "bestie, look here." ๐Ÿ”

The default context is the root of the document. By setting the current context to a lower level object, the application can restrain the search to the descendants of this object. So much more focused. ๐Ÿ‘‘

In the example below, the getElement() method retrieves a paragraph by order number in a previously selected section, and not in the whole document.

my $section = $doc->getElement("//text:section", $s_number);
$doc->currentContext($section);
my $paragraph = $doc->getElement("//text:p", $p_number);

No argument? It literally just gives you back the previous current context, bestie. โœจ Also peep resetCurrentContext().


decode_text(utf8_string)

๐Ÿšจ Caution: this method is a non-exported class method. You gotta call it like this:

OpenOffice::OODoc::XPath::decode_text($utf8_string);

and not from an OODoc::XPath instance. Periodt.

It decodes a UTF-8 string and returns an 8 bit character translation of it out of the user's character set, as defined by this variable:

$OpenOffice::OODoc::XPath::LOCAL_CHARSET

The default value is 'iso-8859-1'. See the Perl/Encode manual for the list of supported character sets. ๐Ÿ“š

OpenDocument uses UTF-8 XML encoding. ๐Ÿ’…

Explicit calls to this method should be rare. It is used internally by methods which return text extracted from document content (e.g. getText).

Warning to contributors: any method which returns text extracted from ODF documents is based on decode_text; so any modification or improvement of the decoding logic should be made there. ๐Ÿ‘‘

encode_text(editable_string)

Class method. ๐Ÿ’ซ

Encodes "local" character strings (for writing to ODF documents).

Example:

$string = OpenOffice::OODoc::encode_text($local_string);

The local character string is defined by the following global variable:

$OpenOffice::OODoc::XPath::LOCAL_CHARSET

for which the default value is 'iso-8859-1'.

Explicit calls to this method should generally be avoided. It is used internally by methods which insert text or attribute values into documents (e.g. setText).


dispose()

Deletes the calling document object. Recommended as soon as the object is no longer needed by the application, and sometimes mandatory to avoid memory leaks, especially in long-running processes. ๐Ÿงนโœจ


exportXMLBody()

Returns the XML string for use by another application representing the body of a document, without UTF8 decoding.


exportXMLContent()

See getXMLContent().


exportXMLElement(path, position)

exportXMLElement(element)

Returns the XML string which represents a particular document element (style definition, paragraph, table cell, object, etc.) for use by another application without UTF8 decoding. ๐Ÿ“ฆ

This method is principally designed to allow remote exchanges of elements between programs using any XML storage or transfer method. It acts as "sender" whilst the "receiver" can use appendElement or insertElement (for example) to insert any exported elements into a document. Example:

# sender programme
# ...
open (EXPORT, "> transfer.xml");
print EXPORT $doc->exportXMLElement('//text:p', 15);
close EXPORT;

# receiver programme
# ...
open (IMPORT, "< transfer.xml");
$doc->appendElement('//office:body', 0, <IMPORT>);
close (IMPORT);

In this example, a paragraph is transferred but it could just as easily be any content, presentation or metadata element.

Conversely, this method is not needed when transferring an element from one document to another in the same program (or from one document position to another). An element can be copied directly from within the same program by reference or replication without going via its XML (see appendElement(), insertElement() and replicateElement()).


extendText(path, position, text [, offset])

extendText(element, text [, offset])

Appends the given text to the previous content of the given element. If the optional 'offset' element is provided, the new element is inserted at the given position.

Example:

$doc->setText($p, "Initial content");
$doc->extendText($p, " extended");

Assuming $p is a regular text element (ex: a paragraph), its content becomes "Initial content extended".

If the second argument is an element itself, it's appended as is to the first element. This feature can be used, for example, in order to append sequences of repeated spaces:

$doc->setText($p, "Begin");
$spaces = $doc->spaces(6);
$doc->extendText($p, $spaces);
$doc->extendText($p, "End");

After the code sequence above, the $p element contains:

"Begin      End"

knowing that a single string containing repeated spaces could not be properly processed by extendText(), even if the 'multiple_spaces' property is set (this property affects the setText() method only).

(See also setText()).


findElementList(element, filter [, replacement])

Returns all the children of the given element whose content matches the given filter (regexp). ๐Ÿ”

If the third argument ('replacement') is given, every string which matches the filter in each child element will be replaced by this 'replacement' value. This 'replacement' argument can be a character string or a function reference. (See replaceText() method below.)

Filtering and possible replacement only affects an element's content and not its attributes.

This method is mostly for internal use. We recommend using other methods for the selective extraction of elements. ๐Ÿ’โ€โ™€๏ธ


flatten(element)

Converts in place the content of the given element to a flat string, removing any structure. Same as $element->flatten() (see flatten() in the "Element methods" section below). If no element is provided, "flattens" the current context element, which is, by default, the root of the document (be careful !). โš ๏ธ


getAttribute(path, position, name)

getAttribute(element, attribute_name)

Returns the value of a given attribute in a given element.

The element is the first argument, the name of the attribute the second one. The return value is undef if the attribute is not defined in the given element.

Example:

my $element = $doc->getElement('//text:p', 15);
my $style = $doc->getAttribute($element, 'text:style-name');

returns the style for paragraph 15.

If the given attribute name doesn't include a namespace prefix, the namespace of the attribute is automatically supposed to be the same as the namespace of the element. In addition, any blank space within the attribute name is regarded as a '-'. So, the same example could be written more concisely as shown below:

my $element = $doc->getElement('//text:p', 15);
my $style = $doc->getAttribute($element, 'style name');

getAttributes(path, position)

getAttributes(element)

Returns a list of the element's attributes in the form of a hash whose keys are the attributes' XML names.


getBody()

Returns the root of the document body. The document body is the main container of all the displayable content not including page headers, page footers, and page backgrounds. ๐Ÿ“„


getDescendants(tag [, context])

Returns the list of the descendants of the given context element matching the given tag. Example:

my $section = $doc->getSection("SectionName");
my @paragraphs = $doc->getDescendants('text:p', $section);

Here, @paragraphs is the list of all the paragraphs which are the descendants (at every level) of a given section (the getSection() method is described in the OpenOffice::OODoc::Text chapter).

If the second argument is not provided, the current context of the document is used (see currentContext()).


getElement(path [, position [, context]])

This method is provided in order to allow the user to retrieve any element in any kind of XML document (ODF-compliant or not) using an application-provided XPath expression. It should be used with elements whose type is not explicitly supported by the more focused (and more user-friendly) methods, described in other manual chapters (::Text, ::Styles, ::Meta, and ::Document). ๐ŸŽฏ

This method returns an element's reference.

The position argument is used to select a particular element, in the order of the document, knowing that the given XPath expression could select a set of elements. Without it, getElement() returns the first element matching the given XPath.

The XPath expression applies in the current context, and not always in the whole document (see currentContext()). However, if the reference of a previously selected element is provided as a third argument, the given element is used as the context.

Position indicators start at 0 just like in Perl tables (and some other programming languages).

Example:

my $p = $doc->getElement('//table:table', 0)

indicates an element containing the first table of a text document or first sheet of a spreadsheet.

Positions can also be counted backwards from the end by giving negative values, i.e. position -1 being the last element. Thus:

my $h = $doc->getElement('//text:h', -2);

indicates the second-last header of a text document.

Note: None of the two examples above should be used in a real application, knowing that the ::Text module provides getTable() and getHeading() that do the job without XPath coding.

When successful, this method ensures that the returned object is indeed an element and not another type of node (e.g. attribute, text, comment, etc.). Such an object is never a printable text; it's either a text container (whose content may be extracted using getText() or getFlatText()) or a non-text element (such as a style, a font declaration, a variable field, a document properties container, etc). ๐Ÿ’–

Limit: getElement() doesn't implement the full XPath specification, while it supports a large subset (see the XML::Twig documentation for details about the current XPath coverage). ๐Ÿ’…

โœจ getElementByIdentifier(id [, options]) โœจ

Returns an element according to the given identifier, if any, or undef otherwise. ๐Ÿซถ

Note that, according to the ODF 1.1 standard, some elements have identifiers (i.e. text:id attributes), while most haven't, so this method can't work with any object. ๐Ÿ’€

Allowed options are:

  • tag => restricts the search to a given element tag
  • context => restricts the search to a given context

Example:

$section = $doc->getElement('//text:section', 0);
$note = $doc->getElementByIdentifier(
            "id004",
            tag             => 'text:note',
            context         => $section
            );

This sequence selects the note (i.e. footnote or endnote) identified by "id004" if such a note appear in the first section of the document. Without the 'context' option, the search space would be the current context (that is the whole document by default). Without the 'tag' option, the first object that owns the given identifier is selected, whatever its tag. ๐ŸŽ€

See also getIdentifier(), setIdentifier(), identifier(). ๐ŸŒธ


๐Ÿ’– getElementList(path [, context]) ๐Ÿ’–

Returns a list of all elements at a specified path. โœจ

Example:

my @ref_summary = $doc->getElementList('//text:h');

The above example returns a table containing all header elements of a text document. ๐Ÿ“š

The path can of course be a more complex XPath expression stipulating, for example, a selection of attribute values. In most cases, you should avoid complicating things unnecessarily (especially in Text, Image and Styles modules), as there are methods for searching by element type, attribute and content which are much easier to use and avoid the need to supply XPath expressions. ๐Ÿ’…

An optional context argument may be provided in order to restrict the search space. ๐Ÿ”

Note: the returned list contains elements in the sense of getElement() and not a list of element contents. ๐Ÿซถ


๐Ÿฆ‹ getFirstTextRun(path, position) ๐Ÿฆ‹

๐Ÿฆ‹ getFirstTextRun(element) ๐Ÿฆ‹

Returns the first text segment of an element whose text content is segmented due to one or more child elements. In other words, returns the beginning of the text content up to the first child element, if any. If the given element just contains flat text, without any child element, returns the whole text, just like getText() introduced below. โœจ


๐ŸŒธ getFlatText(path, position) ๐ŸŒธ

๐ŸŒธ getFlatText(element) ๐ŸŒธ

Like getText() below, but without rendering of possible tab stops, line breaks, repeated spaces, or any other markup. The returned text is just a decoded flat string. โœจ


๐Ÿ–ผ๏ธ getFrameElement(name/number) ๐Ÿ–ผ๏ธ

Selects the frame identified by the given name, or by the given order number in the document context. ๐Ÿ’…


๐Ÿ†” getIdentifier(path, pos) ๐Ÿ†”

๐Ÿ†” getIdentifier(element) ๐Ÿ†”

Returns the identifier (text:id) of the given element, if any. ๐ŸŒธ

See also identifier(), setIdentifier(), selectElementByIdentifier(). ๐Ÿ’–


๐Ÿ˜ณ getNodeByXPath(xpath_expression) ๐Ÿ˜ณ

๐Ÿ˜ณ getNodeByXPath(xpath_expression, context) ๐Ÿ˜ณ

๐Ÿ˜ณ getNodeByXPath(context, xpath_expression) ๐Ÿ˜ณ

A low-level method which returns the node corresponding to the given XPath expression, if it exists in the document. This method (which gives unrestricted access to the entire content of a document) is designed for use with the unexpected. You will obviously need to be familiar with XPath syntax (not documented here) as well as OpenDocument structure. See also selectNodesByXPath(). ๐Ÿซถ


๐Ÿ“ getObjectCoordinates(object) ๐Ÿ“

Returns the coordinates (X, Y) of the target object, if any. This method makes sense with "positioned" objects, i.e. with frames and frame-like objects (images, text boxes). ๐Ÿ“

In an array context, the coordinates are returned as two distinct strings (horizontal, then vertical position). In a scalar context, the values are returned in a single string, and separated by a comma. ๐Ÿ’…

See createFrameElement() for details about the coordinates and size units and notation. โœจ


๐Ÿ“ getObjectDescription(object) ๐Ÿ“

Returns the litteral description of a visible object. This method makes sense for frames or frame-like objects (such as images or text boxes). ๐ŸŽ€


๐Ÿท๏ธ getObjectName(element) ๐Ÿท๏ธ

Returns the name of the given element, if any. ๐ŸŒŸ


๐Ÿ“ฆ getObjectSize(object) ๐Ÿ“ฆ

Returns the size of the given object, if any. This method works with frames and other frame-based objects, such as images and text boxes. ๐Ÿ“

In the returned data, the width comes first, followed by the height. ๐Ÿ“

The size is returned in the same way as the coordinates with getObjectCoordinates(). ๐Ÿ’…


๐Ÿ“„ getPartName() ๐Ÿ“„

Returns the name of the document part, i.e. 'content', 'styles', 'meta', and so on. โœจ


๐ŸŒณ getRoot() ๐ŸŒณ

Returns the absolute root element of the document. The root element contains any other visible or non visible object, including the document body (see getBody) and style definitions. ๐Ÿซถ


โœจ getText(path, position) โœจ

โœจ getText(element) โœจ

Returns text in the local character set, possibly UTF-8 decoded, contained in the element given as an argument (by path/position or by reference). See also getFlatText(). ๐ŸŒธ

Two equivalent examples:

# version 1

my $element     = $doc->getElement('//text:p', 4);

my $text        = $doc->getText($element);

# version 2

my $text        = $doc->getText('//text:p', 4);

Version 2 is better if the only aim is to get the text from paragraph 4. Version 1 is better, however, if during the course of the program you want to perform other operations on the same paragraph. Giving an element's reference will mean avoiding element handling methods having to recalculate a reference from the XPath path. ๐Ÿ’…


๐Ÿ“œ getTextList(path) ๐Ÿ“œ

Returns text from all elements in the specified path. โœจ

Example:

my $summary = $doc->getTextList('//text:h');

my $report = $doc->getTextList('//text:span');

The $summary variable contains a concatenation of all headers. $report contains all the words or character strings that "stand out" which the user has designated by their context, e.g. words in italics in a non-italic paragraph. ๐ŸŽ€

In a list context, the returned data is a table, each of whose elements contains the text of an XML element. In a scalar context (as in our two examples), the returned value is a unique piece of editable text and each element's content is separated from that of the following element by a line feed. ๐Ÿ“


๐Ÿ”– getTextNodes(context [, filter]) ๐Ÿ”–

Returns the text nodes belonging (at any level) to the given context element. So-called text nodes are low-level text runs, without attributes, that populate text containers such as paragraphs, knowing that a paragraph may contain one or more text nodes. For an example, as soon as a bookmark is put within a pararaph, there is (at least) one text node before the bookmark and another one after the bookmark. ๐Ÿซถ

The textnodes are returned as a list in the order of the context. ๐Ÿ“‹

Note that a text node is not an element, but that every text node in a regular document is a child of a text element (generally a paragraph, a heading or a text span). So, the node-based parent() method may be used to get the element that contains a given text node. ๐Ÿง 

The second argument (optional) specifies a search filter. If it's provided, only the matching text nodes are returned. ๐Ÿ”

The example below uses getTextNodes() in order to count the text nodes that contain "foo" and that belong to elements whose style is "bar" in the whole document body (beware, this examples uses methods which are introduced in the OpenOffice::OODoc::Text manual chapter): โš ๏ธ

        my $context = $doc->getBody;
        my @list = ();
        foreach my $tn ($doc->getTextNodes($context, "foo")) {
                my $style = $doc->getAttribute
                                ($tn->parent, 'style name');
                next unless $style;
                push @list, $tn if $style eq "bar";
                }

๐Ÿ“‡ getUserField(name [, context]) ๐Ÿ“‡

Returns the element (if defined) representing a user-defined field, and corresponding to the given name. See also userFieldValue(). โœจ

By default, this method works with the first user field declaration matching the given name in the whole document. However, if the calling object is a 'styles' document part, the search is restricted to a given context (provided through an optional 2nd argument) or to the current context. This feature allows the applications to look for user fields whose declarations are associated to page styles. ๐Ÿ“„


๐Ÿ“‹ getUserFields([context]) ๐Ÿ“‹

Returns the list of the declared user-defined fields. ๐ŸŒธ

The example below prints the names of all the user-defined fields:

        foreach my $field ($doc->getUserFields)
                {
                print $doc->getObjectName($field);
                }

By default, this method returns all the user fields at the document level. However, if the active document part is 'styles', the search is restricted to a given context (provided through an optional 2nd argument) or to the current context. This feature allows the applications to look for user fields whose declarations are associated to page styles. ๐Ÿ“„


๐Ÿ”ค getVariable(name) ๐Ÿ”ค

Returns the user-defined variable identified by the given name. โœจ

[Contribution by Andrew Layton] ๐Ÿ’–


๐Ÿ‘€ getVariableElement() ๐Ÿ‘€

See getVariable(). ๐Ÿซถ


๐Ÿ’พ getXMLContent([filehandle]) ๐Ÿ’พ

Without argument, returns a document's entire XML content. ๐Ÿ“„

Exports the entire XML content of the current member to a flat file, if a file handle is provided. โœจ

Note: the exported data are UTF8-encoded. โœจ

Example, bestie:

open my $fh, ">:utf8", "myfile.xml";
$doc->getXMLContent($fh);
close $fh;

Synonym: exportXMLContent() โ€” same energy, different name, we love a double.


๐Ÿ’ซ getXPathValue

getXPathValue(xpath_expression)

getXPathValue(context, xpath_expression)

getXPathValue(xpath_expression, context)

Okay so this one is a low-level method that lets you go straight at the value that matches a given XPath expression in a document. Character decoding works exactly the same way it does with getText, no cap. ๐Ÿ’…

Example:

$expression =       '//office:automatic-styles'     .
            '/style:style'                  .
            '[@style:style-name="P1"]'      .
            '/@style:parent-style-name';

print $doc->getXPathValue($expression);

That little sequence prints out the name of the parent style of automatic style "P1" (as long as it actually exists in the document, obviously ๐Ÿ™ˆ). And remember โ€” the simpler methods over in the Text and/or Styles modules would literally give you the same result, so don't be extra about it. ๐ŸŽ€

The optional element reference context can be passed as an argument, and it can go either in first or second place, she's flexible like that. When you do that, the search is limited to the chunk of the document tree below that given element. The default search area is the whole entire document. ๐Ÿฆ‹

And just like with the other methods that need XPath paths, this one is primarily for internal use. The majority of applications should not be touching it. It's giving backstage pass, not main character energy. ๐Ÿ˜ญ


๐ŸŒธ identifier

identifier(path, pos [, value])

identifier(element [, value])

Gets or sets the identifier of the given element. Two moods, one method. ๐Ÿ’–

If you don't pass the value argument, it does the exact same thing as getIdentifier(). If you do pass it, the value argument replaces the previous element identifier โ€” or creates one if it wasn't set yet. Glow up. โœจ

Heads up: this method can change the identifier, but it can't remove it. That's not her job โ€” that's setIdentifier()'s whole thing.

See also getIdentifier(), setIdentifier(), getElementByIdentifier().


๐Ÿฆ‹ insertElement

insertElement(path, position, name/xml [, options])

insertElement(element, name/xml [, options])

Inserts a brand-new element before or after the element you point to, either via [path, position] or by reference. Slay. ๐Ÿ’…

If the "name" argument is a literal, a new element with that name gets created and then inserted. If that same argument is a reference to an existing element instead, then that element just gets inserted at the spot you indicated โ€” no new element made. This method is lowkey perfect for adding new elements, or for copying elements from one document to another, or from one position to another inside the same document. ๐Ÿซถ

The position option is how you choose the insertion point of the new element. Possible values are "before", "after" and "within" (and the default is "before", iconic). ๐ŸŽ€

If "position" is set to "within", the new element gets inserted inside the text of the target element โ€” so you also need an additional "offset" option (i.e. a numeric position in the string). Can't skip that one, sorry bestie. ๐Ÿ™ˆ

But honestly, for insertion within a text container, setChildElement() โ€” described later โ€” is much more powerful. Just saying. ๐Ÿ‘‘

Other options are:

text        => "text of element"

attributes  => $attributes

The "attributes" option is itself a hash reference holding one or more attributes in the form [name => value], exactly like in appendElement.

When it works, this method returns the inserted element's reference (else undef). ๐Ÿ“

Example:

my $attributes      =
    {
    'text:style-name'       => 'Heading 2',
    'text:level'            => '2'
    };
$doc->insertElement
    (
    '//text:p', 4, 'text:h',
    position        => 'after',
    text            => 'New section',
    attribute       => $attributes
    );

In a text document, that sequence inserts a level 2 header 'New section' immediately after paragraph 4. Ate. ๐Ÿ”ฅ

The $name argument can be swapped out for an existing element. When you do that, a new reference to the existing element gets inserted, without creating a whole new element. This is how you can display one element at several locations, or in several documents, while it's only held in memory once. But โ€” go read the appendElement section for the consequences of having multiple references to the same physical element. Better to use replicateElement to insert separate copies of an element. ๐Ÿ’€

And under the same conditions as in appendElement, the 'name' argument can be replaced by an XML string describing the element.

Note: to add an element to the end of a document, it would obviously be better to use appendElement(), and to insert an element at a selected position inside an existing element, see setChildElement(). Pick the right tool, babe. ๐Ÿงš

โœจ isOpenDocument()

Returns 1 (true) if the current document is an OASIS Open Document. Use it every single time the application needs to know the format of the document โ€” keeping in mind that some differences between the two formats just can't be completely hidden by the API. ๐ŸŒˆ


๐ŸŽ€ lineBreak

Returns a special line break element, available for insertion within an existing text element โ€” and yes, knowing that "\n" is not recognized as a line break if it's stored "as is". Plot twist. ๐Ÿ˜ญ

The returned element is free, so it could/should be inserted later within a text element. Manifesting that insertion. ๐Ÿ’œ


๐Ÿ‘‘ makeXPath

makeXPath(expression)

makeXPath(context, expression)

Low-level method that allows the creation or direct modification, without restriction (almost) of any document element. It allows "query" expressions in a language similar to XPath. And if the given XPath expression crosses several levels of hierarchy, intermediate nodes can be created or modified "on the fly" โ€” by building the necessary path, which in turn creates the final node. She's building the whole road. ๐Ÿ›ฃ๏ธโœจ

Example:

$doc->makeXPath
 (
 '//office:body/text:p[4 @text:style-name="Text body"]'
 );

That "query" applies the "Text body" style to paragraph 4 in the body of the document. (In reality you will probably never use it, because the setStyle method of the Text module would do the same thing much more simply. Not me doing backflips for nothing. ๐Ÿ™ˆ)

If, like in the example above, a node comes with a position indicator, it cannot be created โ€” it must simply act as a mandatory "passage". So this method can't be used to create, say, an Nth paragraph if there's already an N-1. The math isn't mathing for that. ๐Ÿ’€

The only restrictions apply to namespaces given as prefixes to element and attribute names. They must be defined in the document, i.e. conform to OpenDocument specifications. For the rest? This method allows the creation of almost anything anywhere inside a document. Its use is reserved for OpenDocument XML specialists. Like, actual wizards only. ๐Ÿง™

In its second form, a context node can be given as the first argument. If present, the path is sought (and if necessary created) starting from its position. By default, the path begins from the root. ๐ŸŒฑ

The returned value is the final node's reference (found or created). ๐Ÿ‘‘

The full "query language" syntax used in this method is not documented here. makeXPath is designed to act more as a base for other OpenOffice::OODoc methods than to be used in applications. It's the foundation, not the face. ๐Ÿ’…


๐ŸŒธ moveElements

moveElements(target_element, element_list)

Moves a list of existing elements to a new attachment. ๐Ÿ’ซ

One or more elements get cut from their previous place and appended as children of the target element. Out with the old spot, in with the new. ๐Ÿฆ‹

This method can move elements from one place to another place in the same document, as well as from one document to another one.

Caution: the elements are moved, not copied. So if you wanted a copy, bestie, this is not it. ๐Ÿ˜ญ

๐ŸŽ€ newTextNode

newTextNode(text)

Creates a free text node (to be inserted later within a text element).

A text node is a piece of flat text, without any attribute, that may be a part or the text content of an element. Plain, unbothered, no accessories. ๐Ÿ’…

Note: it's a low level method for special uses. There are various text-oriented methods in the API (mainly described in the ::Text manual page), and the explicit use of text nodes should be avoided. Stay in your lane, diva. ๐Ÿ™ˆ

โœจ objectName

objectName(element [, name])

Returns the name of the given element. And if you hand over a new name as the 2nd argument, it changes the name too. Two-for-one. ๐Ÿ’–


๐Ÿฆ‹ odfLocaltime()

Class method. ๐ŸŒŸ

Converts the numeric time given in the argument to an OpenOffice-compliant date (ISO-8601). The argument type is the same as for the standard Perl localtime() function, i.e. a number of seconds since the "epoch". It can be, for example, a value previously returned by a time() call. โฐ

Without an argument, it returns the current local time in ISO-8601 format. Now o'clock, periodt. โญ

The result of this function can be used as-is in order to set the value of an ODF-compliant date-time element or attribute. Chef's kiss. ๐Ÿ‘‘


๐ŸŒˆ odfTimelocal()

Class method. ๐ŸŒŸ

Translates an ODF-formatted date (ISO-8601) into a regular Perl numeric time format, i.e. a number of seconds since the "epoch". So the returned value can be processed with any Perl date formatting or calculation function. Math girlies rise. ๐Ÿงฎโœจ

Example:

my $date_created = odfTimelocal($meta->creation_date());
$lt = localtime($date_created);
$elapsed = time() - $date_created;
print "This document has been created $date_created\n";
print "$elapsed seconds ago";

That sequence prints the creation date of a document in local time string format, then prints the number of seconds between the creation date and now. So nostalgic. ๐Ÿ˜ญ

Note: the creation_date() method used here works with the meta-data document part only โ€” see OpenOffice::OODoc::Meta for details about this method.
Note: This function requires the Time::Local Perl module. Don't forget your dependency, bestie. ๐Ÿ™ˆ

๐Ÿ’œ odfVersion([new_version])

See openDocumentVersion(). Same vibe, different spelling. โœจ


๐ŸŽ€ ooLocaltime([$time_value])

Class method. ๐ŸŒŸ

See odfLocaltime(). Legacy-era nickname, still serving looks. ๐Ÿ’…


๐Ÿ’– ooTimelocal($oodate)

Class method. ๐ŸŒŸ

See odfTimelocal(). The throwback version. ๐Ÿฆ‹


๐Ÿ‘‘ openDocumentVersion([new_version])

Returns the version of the Open Document Format (ODF) in use in the current document. Hand it an argument and that argument is used to set a new version identifier. simple as that โœจ

โš ๏ธ Okay but bestie, beware: this method does not actually check that your document conforms to any version of the ODF standard. It just retrieves the value of the version number attribute as it has been set by the application which created or modified the document. That's the whole vibe. ๐Ÿ’…

And if you use it to set a new version number declaration, the given value is not checked either. So that value could totally be the number of a real or future ODF version (1.0, 1.1, 1.2, etc), or literally any other arbitrary value you feel like (ex: 99, -1, ...). No notes. ๐Ÿ™ˆ


๐Ÿ“ฅ raw_import(member, source)

Physically imports an external file into an OpenDocument archive associated with an XPath object โ€” but only if it exists, i.e. only if the object was created using file or archive parameters. This method literally just passes the command along to the OODoc::File's raw_import method.

๐Ÿšจ Caution: it must not be used with an "active" element, i.e. an XML member to which the current XPath object or another XPath object is already associated. Also remember that the import is not actually carried out by OODoc::File until a save, so the imported data is therefore not immediately available. Patience, queen. ๐Ÿซถ

๐Ÿ“ค raw_export(member, target)

Physically exports a member from an OpenDocument archive associated with an XPath object โ€” again, only if it exists, i.e. if the object was created using file or archive parameters. This method only transmits the command to the OODoc::File's raw_import method. ๐Ÿ’ซ

๐Ÿ—‘๏ธ removeAttribute ๐Ÿ’…

removeAttribute(path, position, attribute)
removeAttribute(element, attribute)

Deletes the "attribute" attribute (if it's found) of the given element โ€” either by [path, position] or by reference โ€” and returns "true". It has no physical effect and returns undef if the attribute has not been defined or if the element doesn't exist. Nothing happens, no drama, we move on. โœจ

๐Ÿ—‘๏ธ removeElement

removeElement(path, position)
removeElement(element)

Deletes the given element (if it's found) by [path, position] or by reference and returns "true". Returns undef if the element doesn't exist. Gone. Poof. ๐Ÿฆ‹

๐Ÿ—‘๏ธ removeIdentifier

removeIdentifier(path, pos)
removeIdentifier(element)

Deletes the identifier attribute ('text:id') of the given element.

โš ๏ธ Be careful, bestie! This method should be used in order to delete temporary element identifiers that don't comply with the ODF specification. Remember that the identifier is mandatory for some elements, so deleting it there is not the move. ๐Ÿ™ˆ

See also getIdentifier(), setIdentifier(), identifier(). ๐Ÿ‘‘

๐Ÿ” replaceElement

replaceElement(path, position, replacement [, options])
replaceElement(old_element, new_element [, options])

Deletes the given element by [path, position] or by reference and inserts another element in its place โ€” either from another location in the same document or from another document. glow up swap โœจ

A new element can be supplied under the same conditions as for insertElement.

By default, or by using the mode => 'copy' option, it is a copy of the new element which is inserted. With the mode => 'reference' option, it is only a reference which is inserted. See the section on appendElement for comments on the subject of multiple references to a single physical element. ๐Ÿ’œ

๐Ÿงฌ replicateElement(original_element, position_object [, options]])

Makes a copy of the first given element and inserts it into the current document at a position which depends on the second argument and an optional parameter.

If the second argument is an existing object in the document, then the copy is inserted according to an optional 'position' parameter:

  • if no 'position' option is provided, then the copy is appended as the last child of the position object;
  • if 'position' => 'before' or 'after', then the copy is inserted at the same hierarchical level as the position object, according to the same logic as for insertElement().

If the second argument is not an object, but simply 'end', then the new element is appended as the very last child of the physical root of the document. See getRoot(). This option should generally be avoided. ๐Ÿ˜ฌ

If the second argument is given as 'body', then the new element is appended at the end of the document body (see getBody), as it was created through appendElement().

Example:

    my $template = $doc_source->selectElementByAttribute
                (
                '//style::style',
                'style:name',
                'Text body'
                );
    my $position = $doc_target->getElement
                ('//office:styles', 0);
    $doc_target->replicateElement($template, $position);

This sequence adds a style 'Text body' to the style set of $doc_target which copies exactly the style of the same name in $doc_source. Obviously, the section of code dealing with the search for the element to copy and its position is the most laborious. (In a real application, thanks to OODoc::Styles, a more user-friendly coding would be allowed for style replication.) ๐ŸŒธ

This method creates a new element which is an exact copy of the given element, but which is physically separate from it.

This method is slower than simply modifying an existing element or inserting an element reference.

If the user needs only a "free" copy of the element (out of the document structure, to be later attached), the XML::Twig::Elt copy() method should be preferred:

    my $new_element = $old_element->copy;

๐Ÿ”„ resetCurrentContext()

Resets the search context to its default value, which is the root of the document. See currentContext(). Fresh start energy. ๐Ÿงš

๐Ÿ’พ save([filename|filehandle])

Saves the content of the current document through a physical output โ€” that is either a regular file specified by path/name, or an open, application-provided IO::Handle. If no argument is provided, the document which had been used as the source (if any) is used as the default target. ๐Ÿซถ

Technically, as soon as the document container is a regular ODF file, this method is a stub for the save() method of the associated OpenOffice::OODoc::File object, so all the related explanations and recommendations given in the OpenOffice::OODoc::File manual chapter apply. So, for example, be careful if the target is an open IO::Handle instead of a file path/name. โš ๏ธ

The behaviour of this method depends on the way the current OpenOffice::OODoc::XPath object has been created.

If the document is explicitly linked (through the 'file' option of it's constructor) to a regular OOo or OpenDocument file, the document is saved either in the source file, or (if a filename is provided as an argument) in a new file.

If the document is linked to the same file interface as one or more other OpenOffice::OODoc::XPath objects, the behaviour is the same as in the previous case, but all the changes made by all the linked objects are automatically saved in the target file. Example:

        my $content     = odfXPath
                        (
                        file            => 'source.odt',
                        part            => 'content'
                        );
        my $styles      = odfXPath
                        (
                        container       => $content,
                        part            => 'styles'
                        )
        my $meta        = odfXPath
                        (
                        container       => $content,
                        part            => 'meta'
                        );
        # ... a lot of content processing
        # ... a lot of style processing
        # ... a lot of metadata processing
        $content->save('target.odt');

At the end of the sequence above, all the changes made through the $content, $styles and $meta objects are saved in 'target.odt' because these objects share a common file interface. Note that in such a situation, the save() method can be issued from anyone of the objects sharing the file interface (i.e. $content->save could be replaced by $styles->save or $meta->save). one save to rule them all ๐Ÿ‘‘

However, any XML part (content, styles, meta, ...) whose 'read_only' property is set to "true" is not saved. In the example above, if, say, the $meta object is created (through odfXPath()) with a "read_only" option set to "true", only $content and $styles are really saved by the last instruction.

If the document is not associated with a regular OpenDocument compressed file (used through an OODoc::File object), it's saved as "flat XML" to the given file. In such a situation, if the file name is not provided, the source XML file (if any) is used as the target. If the file is "flat XML", OODoc::XPath really effects the physical output, without using any OODoc::File connector. ๐ŸŒˆ

๐Ÿ’ก Note: if you need to save a document as flat XML while it's associated with an OpenDocument file, you should use exportXMLContent() with an application-provided file handle.

๐Ÿ” selectChildElementByName

selectChildElementByName(path, position [, filter])
selectChildElementByName(element [, filter])

Returns the first (or only) element whose name matches "filter" from within the child elements of the given element indicated by [path, position] or by reference.

"filter" is taken to be a regular expression. If several values match the filter, the first of these is returned (in the XML's physical order which is not necessarily the logical order of the document). See the comments about selectElementByAttribute if wanting to select an exact name. ๐Ÿ˜Œ

Returns undef if no elements match the condition.

Returns the first (or only) child (if there are more than one) without anything else if no filter is given or if the filter uses wildcards (".*"). ๐Ÿ’ซ

๐Ÿ” selectChildElementsByName(path, position [, filter])

selectChildElementsByName(element [, filter])

Okay bestie, same energy as selectChildElementByName but it's giving plural โ€” this one hands you back a list of every element that matches the condition. โœจ

Example:

my @search_words =
    $doc->selectChildElementsByName
            ('//text:p', 4, 'text:span');

that returns a list of elements from paragraph 4 which correspond to text which has particular attributes which distinguish it from the rest of the paragraph (colour, font, etc.) ๐Ÿ’…


selectElements([context,] path, filter)

selectElements([context,] path, filter, replacement)

selectElements([context,] path, filter, action [, arg1, ...])

Returns a list of elements corresponding to a given XPath path and whose text matches the filter (regular expression). The context argument, if given, is an element reference which limits the search to its own child elements. The search is carried out in the entire document by default. ๐Ÿฆ‹

An element is selected if the search string is found in its own text or in the text of any element descended from it. E.g. An image element (draw:image) can be selected from the value of its attached "description" field. ๐Ÿ‘‘

You can replace all strings matching the search criteria with the replacement string, on the fly, if the latter is given as an argument after the filter.

Lastly, instead of a replacement string, you can pass a subroutine's reference which will run (in call back mode) each time the search string is matched. If this subroutine returns a defined value, this value is used as the replacement string. The subroutine will automatically receive the rest of the arguments, in this order:

(yeah bestie, the list is literally empty in the source โ€” I'm not making stuff up, I'm just telling you what's there ๐Ÿ™ˆ)

Caution: this method can't retrieve a character string which is split into more than one text element or text span. So, for example, it will never retrieve "My String" as long as "My" and "String" are presented with different styles, even if the two parts of the string belong to the same paragraph. ๐Ÿ˜ญ

If, as is generally the case, you are working exclusively with text elements (paragraphs, headers, etc.), you would be better to use selectElementsByContent() of the Text module which is easier to use and does not require an XPath expression. ๐ŸŽ€

Here is an example which returns the list of images whose descriptors contain the word "landscape" and displays the name of each selected image:

sub printMessage
    {
    my $doc         = shift;
    my $element     = shift;
    my $image = $element->parentNode;
    print "Name: " . $image->find('@draw:name') . "\n";
    }
my @list = $doc->selectElements
    (
    '//draw:image/svg:desc',
    'landscape',
    \&printMessage,
    $doc
    );
Never use this example of code in a real application as it is both purely for demonstration and unnecessarily complex. You can perform the same operation much more simply using the OODoc::Image module. ๐Ÿ’€

selectElementByAttribute(path, attribute [, value [, context [, pos]]])

Like selectElementsByAttribute in a scalar context. By default, returns the first element at the given path which has the given attribute containing the given value. If the value is omitted, then returns the first (or only) element that owns the attribute whatever the value. ๐ŸŒธ

The context optional argument allows one to restrict the search space to a given container. The last optional argument, if set, is a positive integer that specifies the index of the required element if more than one element match the other conditions (beware: if the specified position is out of range, the result is undef). โš ๏ธ

The following example (that apply with the "styles" part of an ODF document) prints a message if the "Time New Roman" font face is declared:

print "The Time New Roman font is defined !"
        if $styles->selectElementByAttribute (
                        'style:font-face',
                        'style:name',
                        "Times New Roman"
                        );

Returns undef if no element matches the conditions. ๐Ÿ™ˆ

See also selectElementsByAttribute().


selectElementsByAttribute(path, attribute [, value [, context]])

Like selectElementByAttribute(), but for an array context. Returns all the elements that match the path/attribute/value/context conditions as a list. โœจ

The following example selects a document section whose name is "Foreword" then selects the list of all the level 3 headings in this section (note that $section is used as the optional context argument in the second instruction):

my $section = $doc->selectElementByAttribute
        ('text:section', 'text:name', "Foreword");
my @headings3 =
        $doc->selectElementsByAttribute
                ('text:h', 'text:outline-level', 3, $section);

(But remember that the same result could be got without knowledge of the XML tags and attributes using more user-friendly methods introduced in other manual chapters !) ๐Ÿ’…

See also selectElementByAttribute().


selectFrameElementByName(name)

Selects the first frame element whose name is exactly the given argument. A frame is an OpenDocument container which can host a rectangular object, such as an image or a text box. ๐Ÿ–ผ๏ธ


selectNodesByXPath(xpath_expression)

This low-level method returns a list of nodes (which are not necessarily elements) which match the give XPath expression. See getNodeByXPath() for options and comments. ๐Ÿงš


setAttribute(path, position, attribute, value)

setAttribute(element, attribute, value)

Modifies or adds an attribute to an element. ๐Ÿ”ง

The element is indicated by reference or by [path, position].

The following arguments are the attribute name and the value.

If the name is provided without namespace prefix, it's automatically concatenated to the element's namespace prefix. Every space in the attribute name, if any, is automatically replaced by a -. โœจ

If the value is undef, the corresponding attribute is deleted if it exists in the element; nothing is done otherwise.


setAttributes(path, position, attributes_table)

setAttributes(element, attributes_table)

Modifies or adds one or more attributes to an element. ๐Ÿ’–

The element is indicated by reference or by [path, position].

The list of attributes is given in the form of a hash name => value.

Example:

my $h = $doc->getElement('//text:h', 12);
$doc->setAttributes(
        $h,
        'text:style-name'   => 'My Header',
        'text:level'        => 3
        );

This sequence gives the 'My Header' style and level 3 to the 13th "header" element in the document. ๐Ÿ‘‘

Any attribute name provided without namespace prefix is automatically concatenated with the namespace prefix of the target element. So, the "text:" prefix could have been omitted in the attribute hash of the example above. In addition, every space in an attribute name is automatically replaced by a -. So the code below produces the same result as the previous example:

my $h = $doc->getElement('//text:h', 12);
$doc->setAttributes(
        $h,
        'style name'        => 'My Header',
        'level'             => 3
        );

An attribute provided as undef is deleted, if it exists. ๐Ÿ—‘๏ธ


setChildElement(context, tag/element [, options])

Creates a new child element within the text content of an existing one. The context element may be provided like with insertElement(), either by [path, position] or directly as the 1st argument. The next argument is the XML tag of the element to be created, or an existing free element. ๐ŸŒฑ

The given context may be any element, including the whole document body; however, it should be a simple text container in most cases.

If the provided tag doesn't include a namespace prefix, it's automatically concatenated with the namespace prefix of the context element (provided as 1st argument). In addition, every space (" ") is regarded as a -. For example, knowing that the ODF names of a line break and a tab stop are respectively 'text:line-break' and 'text:tab', they may be specified as 'line break' and 'tab' when they are inserted in a regular text paragraph (that is their right place). ๐Ÿ’…

For alternative and very specific purposes, the tag argument may be replaced by a function reference. If so, the corresponding application-provided function will be triggered with the following arguments: the containing document, a text node, a position, and possibly a string (this last argument will be provided if setChildElement() is called with a 'replace', 'after', 'before' or 'capture' argument introduced below and will contain the matching substring). The application-provided function is supposed to insert one or more contiguous new elements in the text node at the given position (optionally using the given substring); it must return an element. However, most users may safely forget this feature... ๐Ÿ™ˆ

Allowed options are

  • attributes โ€” attribute/value hash for the new element
  • text โ€” text content for the new element
  • offset โ€” position
  • after โ€” search string (regexp)
  • before โ€” search string (regexp)
  • replace โ€” search string (regexp)
  • capture โ€” search string (regexp)
  • way โ€” search way ('forward' or 'backward')
  • start_mark โ€” element
  • end_mark โ€” element

Okay so some of them are mutually exclusive, bestie. They work according to the following logic. ๐Ÿ’…

By default, the new element is created with no text and no attributes. But you can hand over some initial content through a 'text' optional parameter. And there's an 'attributes' option that lets you pass a whole set of attributes for the new element as a hash reference โ€” heads up though: every attribute name you give without a namespace prefix automatically gets concatenated with the same namespace prefix as the element name you gave. That's the vibe. โœจ

The child element may be inserted at the beginning, at the end, or at a position within the text content. In that last case, the position may be specified by a given numeric argument, or looked for according to a given expression.

By default, the new element is inserted at the beginning of the target element. Want an arbitrary other position? Use the 'offset' argument โ€” it's either an integer (positive or negative) value, or one of the 'start' and 'end' special indicators. If 'offset' is set to 'start' or 'end', the new element is inserted at the start or at the end, and the other position options are ignored. If 'offset' is a negative integer, the position is counted backward from the end.

โš ๏ธ Caution: if the text of the target container includes tab stops and/or multiple contiguous spaces, the effective offset will be larger than the given one (because ODF tab stops and multiple spaces are special markup elements and not characters). Not me getting betrayed by whitespace ๐Ÿ˜ญ

Whatever the value of 'offset', a 'way' option โ€” whose possible values are 'forward' (the default) or 'backward' โ€” specifies the search way. Here's the tea:

  • If 'offset' is negative, the 'way' option is ignored, because the way is always backward.
  • If 'offset' is positive and 'way' is 'backward', then the result is the same as if 'offset' was negative.
  • If 'offset' is 0 or not set and 'way' is 'backward', then the search is done backward from the end.

A search string may be provided instead of or in combination with an offset. If so, the insert point will depend on the position of the first substring that matches the given optional search expression (if any). The search expression may be provided through the 'after', 'before', 'replace' or 'capture' option:

  • An expression provided with 'after' or 'before' means the insert point is immediately after or immediately before the first matching substring.
  • If the search string is provided through 'replace' or 'capture', the matching string will be replaced by the new element. With 'replace' the matching string is just deleted; with 'capture' the same matching string is moved into the new element.

Of course, these search string options are mutually exclusive โ€” if more than one of them are wrongly set, only one is considered, and the priority order is 'after', 'before', 'replace', and 'capture'. And if both 'capture' and 'text' are set, the result is the same as with 'replace' and 'text'. iykyk ๐Ÿ’€

If the insertion point depends on a search string (i.e. if 'after', 'before', 'replace' or 'capture' is used), it's selected according to the first match. But you can reverse the search way using the 'way' option. Also, the search area may be restricted by the 'offset' parameter: if 'offset' is used in combination with any search string option, it specifies the limit of the search area instead of an insertion point.

  • If 'offset' is positive and 'way' is 'forward' (or not set), the search is done from 'offset' to the end.
  • If 'offset' is negative or 'way' is 'backward', the search is done backward from the given offset to the beginning.

The 'start_mark' optional parameter is a child element that already exists within the context element. If this parameter is set, it says the search will start from the position of this child element and not from the beginning or the end of the context element. If the search way is forward, the insert point (in case of success) will be located after the start mark; but if the search way is backward, the insert point will be before the start mark. And if an offset is provided, it's counted from the position of the start mark. ๐ŸŽ€

Another existing child element may be used to restrict the search area, through an 'end_mark' parameter. If this parameter is set, no search will be done beyond it. If both 'start_mark' and 'end_mark' are provided, the search will run from the first one to the second one. And of course, if the start mark is located after the end mark, nothing will be done if the search way is not backward, and vice-versa.


The following example inserts a new 'text:time' element (i.e. an ODF time field) immediately after the first "Clock:" substring appearing between the 20th character and the end of a given paragraph (specified by the 1st argument). The new element will be a 'text:time', knowing that the namespace prefix of a paragraph element (text:p) is "text". According to the given attributes, the field will display the current time increased by 15 minutes:

$doc->setChildElement(
    $paragraph, 'time',
    offset          => 20,
    after           => "Clock:",
    attributes      => {
            'time-value' => odfLocaltime()
            }
    );

The variant below creates the same 'time' field after each occurrence of "Clock:" (probably not very useful, but the aim is to illustrate the use of 'start_mark' in order to ensure that every field but the first one will be inserted after the previous field):

my $field = undef;
while   (
    $field = $doc->setChildElement(
            $paragraph, 'time',
            after           => "Clock:",
            start_mark      => $field,
            attributes      => {
                    'time-value' => odfLocaltime()
                    }
    )
) {}

Note that the loop body is empty โ€” the start mark, which is undef at the first round, is then the previously inserted child element.

โš ๏ธ Caution: without carefully designed offset and/or search option, such a construct may produce a long or infinite loop (until memory fault) ๐Ÿ˜ญ โ€” and in addition, the setChildElements() method (see below) is generally more appropriate for such repetitive element insertions. Bestie, please don't cook yourself here.

The next example creates a text span (i.e. a text area with a special character style) for the last "ODF" substring of a given paragraph:

$doc->setChildElement(
    $paragraph, 'span',
    capture         => "ODF",
    way             => 'backward'
    attributes      => {
            'style-name'    => "My Style"
            }
    );

These examples are shown to illustrate the general logic, not necessarily to be reproduced in real applications, knowing that setChildElement() is a common basis for more specialized methods (mainly introduced in the OODoc::Text man page).

See also splitContent(). ๐Ÿฆ‹


๐ŸŒˆ setChildElements(context, tag/element [, options])

Like setChildElement() but with a repetitive effect that depends on the options.

If 'offset' is the only option, it's used at a regular interval between insert points. But if one of the search string options ('after', 'before', 'capture', or 'replace') is set, 'offset' is used once for all to exclude an area from the search space, and not as an interval between the new elements. The other options work like with setChildElement().

The example below inserts a line break after every ";" in a given paragraph (remember that an ODF line break is an element; it's neither an end of paragraph nor a "\n" character):

$doc->setChildElements(
    $paragraph, 'line break',
    after   => ";"
    );

๐Ÿ’– setFlatText(path, position, text)

๐Ÿ’– setFlatText(element, text)

Like setText() described below, but without translation of "\t" and "\n".

For exceptional use only. Allows, for example, the use of the OODoc API with non-OpenDocument XML files. ๐Ÿงš

โœจ setIdentifier(path, pos, value)

โœจ setIdentifier(element, value)

Sets (or resets) the identifier of the given element. The identifier is namely the 'text:id' attribute, which is allowed for some elements and not for other elements by the ODF standard. OpenOffice::OODoc allows it with any kind of element, and doesn't check its uniqueness, so it may be used with care. A non-conformant element identifier is not an issue if, for example, it's removed before editing or processing the resulting documents through another application.

This method removes the identifier if the value argument is undef; however the removeIdentifier() method produces the same result in a more self-documented way).


๐ŸŽ€ setObjectCoordinates(object, coordinates)

Updates or creates the coordinates (X, Y) attributes of a visible object (ex: image, text box, frame). See createFrameElement() for the coordinates units and notation. ๐Ÿ“


๐ŸŒธ setObjectDescription(object, description)

Updates or creates the literal description of the given object.

Should be used for frames, images or text boxes.

โš ๏ธ Caution: the description is not the same as the printable content of a text box. Don't confuse them bestie, they're giving different things entirely. ๐Ÿ’€

๐Ÿ‘‘ setObjectName(element, name)

Sets or changes the name of the given element according to the given new name. Deletes the name if the given name is undef.


๐Ÿฆ‹ setObjectSize(object, size)

Updates or creates the width and height attributes of a given object.

This method makes sense for visible, rectangular objects only, such as the frames, images or text boxes.

See createFrameElement() for details about the size units and notation.


โญ setRangeMark(type, identifier, parameters)

Creates a pair of corresponding delimiting markup elements right there in place, so you can carve out an identified text range โ€” think a range bookmark, an index mark, or a table of content mark. It's giving "I need to wrap this text and I need it to have a name" energy. ๐Ÿ’–

The first argument tells it what kind of range you're making. It's mandatory, but ngl nobody checks the value โ€” it's vibes only. Legal examples include 'bookmark', 'toc-mark', and 'alphabetical-index-mark'. And here's the cute part: if the type you hand over doesn't contain a semicolon, it gets automatically prefixed based on whatever's in the 'prefix' parameter (whose default is 'text').

The identifier argument is mandatory too โ€” it's an arbitrary (preferably unique ๐Ÿ‘‘) identifier for the pair. Most of the time the end-user never sees it, but sometimes it's an actual explicit name (like in a range bookmark).

The 'prefix' optional parameter lets applications pick a specific XML prefix โ€” and jsyk, the default prefix for range marks is 'text'.

You can also pass an arbitrary set of attributes as a hash through the optional 'attributes' parameter. That hash gets processed using the exact same logic as the common setAttributes() method. ๐Ÿซถ

The 'context' optional parameter, if you provide it, specifies the element (which should be a text container โ€” a paragraph, a heading, or a text span) that holds the text range you're delimiting. But โ€” and this is important, bestie โ€” if the text range you're covering is spread across two or more text containers, you must not set this parameter. Instead you gotta give a separate 'context' parameter for the start mark and the end mark (more on that below ๐Ÿ‘‡).

If (and only if) 'context' is set โ€” meaning the whole text content between the marks lives in the same element โ€” then the optional 'content' parameter lets you hand in an expression. When you do, setRangeMark() hunts for the first substring matching that expression inside the target element, and if it wins, the range marks get inserted at the beginning and the end of that substring. You can restrict the search space of the substring using the 'offset' and 'way' parameters, following the same rules as setChildElement(). Heads up though: the 'replace', 'before' and 'after' parameters don't apply with setRangeMark().

Unless 'context' and 'content' are both defined, you've got two mandatory parameters: 'start' and 'end'. Each one is a hash of parameters that apply to the start mark and the end mark respectively. Each one allows the same options as the option hash of setChildElement(), aka 'offset', 'before', 'after', 'replace' and/or 'way' as described above. And note: the 'start' and 'end' structures get ignored the second 'context' and 'content' are set at the first level. ๐Ÿ™ˆ

On top of that, if the start and end marks aren't in the same text element, you must provide separate 'context' parameters with each one of the 'start' hash and the 'end' hash. However, if the 'end' hash doesn't contain any 'context' parameter, the end mark is assumed to be in the same container as the start mark.

The method returns the new start and end marks as a list of elements in array context, or just the start mark in scalar context. If it fails (wrong parameters, oops), both are undef โ€” and know that setRangeMark() creates the full pair of marks or nothing at all. Also note that the optional attributes (the ones from the 'attributes' parameter) get stored in the start mark element only.

By default, nothing stops an application from creating a range mark whose start point is (temporarily or not) sitting after the end point โ€” which introduces an inconsistency, no cap. But you can set a 'check' boolean option: if it's 'true', an order check happens and if something's wrong, the range mark creation is cancelled and the method fails. On the other hand, as long as your application can guarantee the start will always be set before the end, you should skip the order check for performance reasons. ๐Ÿ’…

โš ๏ธ Caution: The relative positions of the two marks are not checked, so nothing prevents users from creating a range whose start point is (temporarily or not) located after the end point in the document. The applications should ensure that the 'start' and 'end' options really specify two locations in the right order.

The following instruction creates an index mark covering a text area within a single paragraph (previously selected); the range starts before the "abc" substring and ends after the "xyz" substring; the mark identifier is 'ind1234'. Nothing is done if one of these substrings isn't present in the target element:

$doc->setRangeMark(
    'alphabetical-index-mark', 'ind1234',
    element => $paragraph,
    start   => { before => "abc" },
    end     => { after  => "xyz" }
    );

The next example creates a range bookmark (i.e. a bookmark covering a text area) that starts before the "abc" substring in a paragraph and ends at the end of another paragraph:

$doc->setRangeMark(
    'bookmark', 'bm0001',
    start   => { element => $p1, before => "abc" },
    end     => { element => $p2, offset => 'end' }
    );

๐ŸŒธ setText(path, position, text)

๐ŸŒธ setText(element, text)

Uses the given text as the content of the given element. Slay, that's it. โœจ

Any previous content โ€” including formatting markup, bookmarks, notes, references, all of it โ€” gets replaced by the given text. Gone. Poof. ๐Ÿฆ‹

If the text you hand over includes tab stops ("\t") or line breaks ("\n"), they get swapped out for the appropriate OpenDocument tags. If you need to avoid that translation, use setFlatText() instead.

Note: Strings containing repeated whitespaces are not properly processed by default. A sequence of repeated spaces, no matter how long, gets replaced by a single space in the target document. So
$doc->setText($p, "Begin        End");

produces the same visible result as

$doc->setText($p, "Begin End");

You can override this default behaviour with the 'multiple_spaces' document property. If 'multiple_spaces' is set to 'on', the repeated spaces in the example above are properly recorded. However โ€” this optional feature comes at the price of some other features and, above all, it has a negative impact on performance (because of an additional processing of every space ๐Ÿซ ). Of course, temporarily activating the 'multiple_spaces' feature is allowed, like in the following example, which sets a content including multiples whitespaces:

$doc->{'multiple_spaces'} = 'on';
$doc->setText($p, "Begin        End");
$doc->{'multiple_spaces'} = undef;

See spaces() and extendText() for a workaround if you need to insert repeated spaces without using the 'multiple_spaces' property. ๐Ÿซถ


๐ŸŒˆ setUserFieldDeclaration(name [, options])

Creates a new user field declaration in the document. Iconic behaviour honestly.

The optional parameters are:

  • 'type' โ€” data type (default 'string')
  • 'value' โ€” initial value (default "")
  • 'currency' โ€” a 3-letter currency code (ex: EUR, USD...)

See also setTextField() in OpenOffice::OODoc::Text.


โญ spaces(length)

Returns a special element, ready to be inserted within a text element, that represents repeated contiguous blank spaces โ€” because bestie, repeated spaces simply can't be displayed properly by an OpenDocument-compliant application if they're stored as a flat string. The returned element is free, so it could/should be inserted later within a text element. See extendText() for an example of use. ๐Ÿ’–


๐Ÿ’œ splitContent(path, pos, tag, expression [, attributes])

๐Ÿ’œ splitContent(element, tag, expression [, attributes])

Moves some parts of the text content of the given element and its descendants into new child elements.

The tag argument specifies the XML tag of the child elements to be created. Unless this tag comes with a namespace prefix (or, more precisely, unless it contains a semicolon), it gets automatically concatenated with the namespace prefix of the host element.

The following argument is a regular expression that specifies the text substrings to wrap in the new elements. An element is created for every match in the context element and, if there are any, in its existing children.

After these arguments, additional attribute/value pairs may optionally be provided; each one becomes an attribute for every created child element (same name, same attributes, all of them). Every attribute name provided without a namespace prefix is automatically concatenated to the same namespace prefix as the new elements.

This method returns the new child elements as a list. ๐ŸŽ€

Note: splitContent() is a simplified interface for the mark() method provided by XML::Twig, which may be directly used as an element method for more advanced uses.

๐Ÿงš splitElement(element, offset)

Splits a text element at a given offset. This method is a wrapper of the XML::Twig::Elt split_at() method, so, as said by Michel Rodriguez in his documentation, it splits "a text element in 2" at the given offset so "the original element now holds the first part of the string and a new element holds the right part". ๐Ÿฆ‹

On top of that, the new element is created with the same attributes (ex: the style or the heading level, if any) as the original one.

The new element is inserted immediately after the old one.

The method returns both the original and the new elements in a list context. In a scalar context, the new element only is returned. โœจ

โš ๏ธ Caution bestie, read this twice: splitElement() works properly on elements containing "flat text" only. It's a bit complicated to use and probably doesn't produce the right effects on elements containing line breaks, tab stops, "styled spans" or any kind of structure. And if it's used with an element containing more than one text segment, it works with the first one only. ๐Ÿ’€

๐ŸŽ€ tabStop ๐ŸŽ€

Returns a special tabulation mark element, available for insertion within an existing text element (knowing that "\t" is not recognized as a tab stop if stored "as is" โ€” rude, but that's the vibe). The returned element is free, so it could/should be inserted later within a text element. โœจ

๐Ÿ’– userFieldValue(user_field [, value]) ๐Ÿ’–

Reads the stored value of a given user field, or changes it if a value is provided.

The 1st argument can be either the name of the field (as it appears for the end-user) or a previously loaded user field element. See also getUserField(). ๐Ÿซถ

This method doesn't create any new user field. It can only read or update an existing one. No making new friends here, just vibing with the ones you have.

If the given user field is numeric (ex: date, currency) the returned and/or provided value is the internally stored value, and not the displayed one. ๐Ÿ‘€

โš ๏ธ Warning: the changes made in a document using userFieldValue() don't necessarily produce visible changes for the end-user. This method can update the internal value of a field, but the displayable representations of this field are not automatically refreshed (it depends on a later field update). So like... the data changed, the screen didn't, and that's on purpose, not a bug you found ๐Ÿ˜ญ

๐ŸŒธ variableValue(name/element [, newvalue]) ๐ŸŒธ

Returns the current value of the given user-defined variable or, if a new value is provided as the second argument, updates the variable accordingly.

[Contribution by Andrew Layton] ๐Ÿ’œ


โœจ Element methods โœจ

Every document element is an OpenOffice::OODoc::Element object, and OpenOffice::OODoc::Element inherits all the rich features of XML::Twig::Elt, including the very powerful copy(), cut(), paste(), move() and replace() methods (look at the XML::Twig documentation for details โ€” the lore is all there). Some additional methods, provided in the ::Element package, are described below.

The "element methods" should be regarded as reserved for advanced uses, possibly in combination with native XML::Twig::Elt methods (not documented here, but the XML::Twig package itself is well documented).

Remember these methods belong to the element and not to the document...! ๐Ÿ‘‘

๐Ÿ‘‘ appendChild(newnode)

Appends a node as the last child of the calling node.

  • If the argument is an existing node, it's appended as is.
  • If the argument is a string, a new node is created, with the given string as the XML tag name.

๐Ÿงš appendTextChild(text)

Appends a text node (PCDATA) as the last child of the calling element.

๐Ÿ”ฅ flatten()

Converts in place the content of the calling element to a flat string, removing any structure. All the children of the calling element are removed and their text content is concatenated. The resulting string becomes the only content of the element. So if the calling element is a table, the tabular structure disappears and gets replaced by the concatenated contents of all the cells. Any possible internal tab stop or line break element is removed, as well as any "styled" text span (see setSpan() and removeSpan() in the OODoc::Text chapter for the info about styled text spans).

Be careful, a lot of elements are not displayed by the OpenDocument compliant software. For example, a section element becomes invisible if it directly contains its text, without structure elements such as paragraphs, headings, tables, and so on. In order to make visible the "flattened" content of a previously complex element, the XML tag should be replaced by the tag of a "displayable" element. In the following example, a section is flattened, then tagged as a paragraph, so its content remains visible:

my $s = $doc->getSection("AnySection");
$s->flatten;
$s->set_tag('text:p');

Note: getSection() belongs to OpenOffice::OODoc::Text and set_tag() is provided by the underlying XML::Twig::Elt package.

The text flattening is sometimes required in order to allow the applications to retrieve strings which are split into more than one text container. For example, a string such as "OpenDocument" can't be retrieved using selectElements() or any other string search method of the API if, say, "Open" and "Office" don't belong to the same text span (i.e. if they have different styles; look at setSpan() in OpenOffice::OODoc::Text to know more about text spans). In such a situation, flatten() removes any text span markup, so the whole text content of the element can be processed as a regular character string. ๐Ÿฆ‹

โš ๏ธ Caution, this method can produce terrific results when misused. Terrific as in terrifying. Respect the flatten().

๐Ÿ’… getLocalPosition([regexp])

Returns the position of the current element in the list of all the children of the same parent with the same type.

Example:

$cell->getLocalPosition();

Assuming $cell is a table cell, this example returns the position of the cell in the row without counting the covered cells (if any).

If a regular expression is provided as the optional argument, all the siblings matching the expression are counted; but the method returns zero if the calling element itself doesn't match the expression.

Example:

$cell->getLocalPosition(qr'table:(covered-|)table-cell');

returns the position of the cell among all the cells (covered or not) in the row.

Note: This method is a wrapper of the pos() method of XML::Twig::Elt, but the returned values are zero-based in order to be consistent with the other element addressing features of OpenOffice::OODoc.

๐ŸŒˆ insertNewNode(xml_tag, position_flag [, offset])

Creates a new XML element, whose tag is passed as the 1st argument, before, after or within the calling element. The 2nd argument must be set to 'before', 'after', 'within', or any other value accepted by the paste() method of XML::Twig. If the 2nd argument is 'within', a 3rd one must be provided and indicate the offset.

๐Ÿ“ replicateNode(count, position)

Produces one or more copies of the calling element and inserts the copies before or after it. The position argument should be 'before' or 'after'; its default is 'after'. Technically, the position argument could be any one of the position options of the XML::Twig::Elt->paste method, including 'first_child', 'last_child' or 'within'; but any other than 'before' and 'after' probably don't make sense in an OpenDocument-compliant data structure.

Without any argument, the calling element is replicated once. But if the count argument is provided and set to zero or a negative value, nothing is done. (Iconic behaviour honestly, zero means zero.)

Example:

my $row = $doc->getTableRow("Table1", -1);
$row->replicateNode(5);

This sequence appends 5 more rows to a table; each new row is a copy of the last original row, including each individual cell and its content. Clone army, but make it spreadsheets. ๐Ÿ‘‘

๐Ÿ’œ selectChildElement(filter)

Like selectChildElements() below, but returns only the first node matching the filter.

Note: the first_child() method of XML::Twig::Elt should be preferred when the filter is the exact tag name of the needed element.

๐ŸŽ€ selectChildElements(filter)

Selects the children with XML tag names matching a given filter. The filter is processed as a regexp.

Note: the children() method of XML::Twig::Elt should be preferred if the filter is the exact tag name of the needed elements.

โญ textLength()

Works with text nodes. In array context, returns the length of the text and the text itself; in scalar context, returns the length only.


๐Ÿฆ‹ Properties ๐Ÿฆ‹

No class variables are exported; the applications, if needed, must access them using their full name ($OpenOffice::OODoc::XPath:XXX).

The following names should be prefixed explicitly with "$OpenOffice::OODoc::XPath::"

๐Ÿ”น CHARS_TO_ESCAPE

contains the list of reserved characters which, in XML, should be replaced by escape sequences.

๐Ÿ”น OO_CHARSET

indicates the character set used for OpenDocument document encoding and whose default value is 'utf8' (it should not be changed). Leave it alone bestie, it's fine as it is. ๐Ÿซถ

๐Ÿ”น LOCAL_CHARSET

indicates the user's character set, by default 'iso-8859-1'; it must be changed according to the real user's needs (warning: there is no kind of automatic adaptation to the user's locales, so the application must explicitly load the right value in this variable); it should be done using the odfLocalEncoding() accessor (see the OpenOffice::OODoc man page and, for the list of supported character sets, the Encode module's documentation).

The content of these three variables should not normally be directly modified by the applications. Hands off the internals, babe. ๐Ÿ™ˆ

Instance hash variables are:

'container'         => <oodoc_file_object>
'file'              => <OpenDocument file>
'part'              => <name of the XML part in the ODF package>
'readable_XML'      => <'true' or 'false'>
'local_encoding'    => <user's output encoding>
'multiple_spaces'   => <'on' or undef, see setText()>
'element'           => <name of loaded XML element>
'xpath'             => <XML::Twig, XPath-capable object>
'twig_options'      => <XML::Twig options as a hash reference>
'opendocument'      => <'true' or 'false'>

However, the 'xml' variable is cleared almost immediately after a successful constructor call, in order to save memory. As soon as the corresponding XPath object has been created, the XML source is no longer required. Poof, gone, memory saved, main character energy. โœจ

okay bestie, quick lore drop on the 'xpath' variable โ€” this is the main character of the whole vibe. If you have an OODoc::XPath object, its 'xpath' variable holds a reference to the document structure exactly as it's handed over through XML::Twig (go read the CPAN docs, iykyk). This object? It encompasses the entire current XML tree. Every single time you touch XML through OODoc::XPath objects, you're doing it via XML::Twig. No side quests.

So like, once you've run this:

my $xp = $doc->{'xpath'};

โ€ฆthe experienced programmer can use $xp to reach all the functionality of the XML::Twig API. โœจ But โ€” and this is the part you cannot skip, bestie โ€” remember that every operation you do through this interface has a direct effect on the content of the $doc object. It's not a copy. It's the same tree. Act accordingly. ๐Ÿ’…


'twig_options' lets you hand over a hash reference of extra options for XML::Twig. These options can change the way the document gets parsed while odfXPath() is running. For special applications only, okay? Go see the XML::Twig reference manual, it's giving required reading. ๐Ÿ“š

'opendocument', when it's true, means the document is declared as an OASIS Open Document. If it's false or undef, then the document format is OpenOffice.org version 1. ๐Ÿซถ

Do not change this property. Like, don't. The reason is that OpenOffice::OODoc can't change the format of an existing document โ€” so flipping this switch is not the slay you think it is. ๐Ÿ’€

The legends behind this, in order of appearance:

  • Developer/Maintainer: Jean-Marie Gouarne โ€” <http://jean.marie.gouarne.online.fr> โ€” and yes, that's the whole URL, no cap.
  • Contact: jmgdoc@cpan.org ๐Ÿ“ง
  • Copyright 2004-2010 by Genicorp, S.A. โ€” <http://www.genicorp.com> ๐Ÿ‘‘
  • Initial English version of the reference manual by Graeme A. Hunter (graeme.hunter@zen.co.uk). ๐ŸŽ€
  • License: GNU Lesser General Public License v2.1 โ€” read it, respect it, periodt. โญ

And that's a wrap on this whole page, bestie. ๐Ÿฆ‹ This was OODoc::XPath(3pm), from perl v5.34.0, dated 2022-06-16. Go forth and parse some XML, you absolute icon. ๐Ÿ’œ๐ŸŒธ

๐Ÿ“ฆ where this page comes from