Categories > OpenTBS with DOCX >

Automatic Subblocks - no dot operator?

The forum is closed. Please use Stack Overflow for submitting new questions. Use tags: tinybutstrong , opentbs
By: Sarah
Date: 2012-04-04
Time: 23:29

Automatic Subblocks - no dot operator?

I am working on a project which has a an array of several objects, each of which creates a table in my template (essentially making pages in a .docx file). I am using a main block which is set up like:

[MainBlock;block=w:tbl; sub1=type;sub2=quantity;sub3=lineNum;sub4=specs.length;]

...to iterate through the array. w:tbl is the main table the takes up the whole page.

My mergeBlock() looks like:

$TBS->MergeBlock('MainBlock', $lineItem);

Where $lineItem is an object which looks like:

$lineItem->type = 'oblong';
$lineItem->quantity = '13';
$lineItem->lineNum = '6';
$lineItem->specs = array('width' => '35', 'length' => '15', 'weight' => '120');

I want to be able to access $lineItem->specs->length in a different area than the other parts, and I want to know if there is any way to use the dot (.) operator syntax used everywhere else, in the _sub# declarations of MainBlock.

Or is there another way?

Thank you for reading!
By: Skrol29
Date: 2012-04-05
Time: 03:24

Re: Automatic Subblocks - no dot operator?

Hi,

Your Word table could be something like this:

------------------------------------------------------------------------------
| Type:                           | [MainBlock.type;block=w:tbl;sub1=specs]  |
------------------------------------------------------------------------------
| Quantity:                       | [MainBlock.quantity]                     |
------------------------------------------------------------------------------
| LineNum:                        | [MainBlock.lineNum]                      |
------------------------------------------------------------------------------
| Key                             | Val                                      |
------------------------------------------------------------------------------
| [MainBlock_sub1.key;block=w:tr] | [MainBlock_sub1.val]                     |
------------------------------------------------------------------------------

Parameter "block=w:tbl" tells that the Word table is the bounds for the main block.
Parameter "sub1=specs" tells that on this main block, there is a sub-block to merge with data in column "specs".
The fields "MainBlock_sub1.key" is to display the key value of the data in the sub-block. ("key" and "val" are keywords for data that is a simple array)
Parameter "block=w:tr" tells that the row is the bounds for the sub-block.
By: Sarah
Date: 2012-04-05
Time: 21:06

Re: Automatic Subblocks - no dot operator?

Thank you so much for getting back to me so quickly!

I am thrilled to learn I can use [MainBlock.quantity] syntax for the fields that have only on entry - this is much simpler than my long list of sub-blocks!

I am still caught up on how to deal with the specs array. Say my array looked more like:

$lineItem->specs = array('width' => array('value', 'positionX', 'positionY'),
                       'length' => array('value', 'positionX', 'positionY'),
                       'weight' => array('value', 'positionX', 'positionY'));

Where positionX and positionY are used to position a text box like:

<v:shape style="position:absolute;left:[positionX];top:[positionY];width:600;height:300;">
   <w:txbxContent>
      <w:t>[value]</w:t>
   </w:txbxContent>
</v:shape>

Where each of the columns in the array fill a field in the repeating area (the textbox). I have looked through the manual (which is very helpful), but I cannot find anything about sub-sub-blocks, if they are possible. Is this possible? Is there a better approach? This class has been so easy to use, I know I'm just missing something.

Thank you again for your help! I really appreciate you taking the time to respond.
By: Skrol29
Date: 2012-04-05
Time: 22:35

Re: Automatic Subblocks - no dot operator?

Hi,

Yes sub-sub-block are possible, you just have to consider [MainBlock_sub1] as a main block.

But if you always have 3 items in your "specs" array, why don't you just use the syntax:
  [MainBlock.spec.width.0] for value of the width item,
  [MainBlock.spec.width.1] for x position of the width item,
  [MainBlock.spec.width.2] for y position of the width item,
This requires that you have three <v:shape> in your template.

But your snippet with the <v:shape> entity shows that you have to merge some values inside the XML source.
For this purpose, you can use parameter "att" which makes a TBS filed to move to an attribute value.
But for this, the value to merge must be the new value of the attribute, so you can use the "ondata" parameter to build the string for the value of attribute "style".

It could be something like this :

PHP :
function f_mainblock($BlockName, &$CurrRec) {
    $CurrRec['spec']['width']['xstyle'] = "position:absolute;left:".$CurrRec['spec']['width'][1].";top:".$CurrRec['spec']['width'][2].";width:600;height:300;"
    $CurrRec['spec']['length']['xstyle'] = "position:absolute;left:".$CurrRec['spec']['length'][1].";top:".$CurrRec['spec']['length'][2].";width:600;height:300;"
    $CurrRec['spec']['weight']['xstyle'] = "position:absolute;left:".$CurrRec['spec']['weight'][1].";top:".$CurrRec['spec']['weight'][2].";width:600;height:300;"
}

DOCX:
...
[MainBlock.type;block=w:tbl;ondata=f_mainblock]
...
here is the v:shape #1: <v:shape ... > *** [MainBlock.specs.width.0] [MainBlock.specs.width.xstyle;att=v:shape#style] **** </v:shape>
here is the v:shape #2: <v:shape ... > *** [MainBlock.specs.length.0] [MainBlock.specs.length.xstyle;att=v:shape#style] **** </v:shape>
here is the v:shape #3: <v:shape ... > *** [MainBlock.specs.weight.0] [MainBlock.specs.weight.xstyle;att=v:shape#style] **** </v:shape>
By: Sarah
Date: 2012-04-06
Time: 00:22

Re: Automatic Subblocks - no dot operator?

Thank you again for replying so quickly!

Unfortunately, I don't always have 3 items in my specs array, though each row in the specs array will have a set number of arguments (e.g. 'value', 'positionX', 'positionY').

To give a little more detail: each dimension (width, height, etc.) has two lines spaced apart with an arrow between and a textbox in the center like:

|                                           |
|<----------------Width-------------------->|
|                                           |

I am trying to position these lines (and arrow and textbox) with some php variables I have created relative to various images I am creating. It seems like what I need to do is use a sub-block of MainBlock like:

DOCX:
[LineItem;block=w:tbl;sub1=specs]

<v:line from="[LineItem_sub1.width.positionFrom;block=v:line/]" to="[LineItem_sub1.width.positionTo;block=v:line/]" />
<v:line from="[LineItem_sub1.length.positionFrom;block=v:line/]" to="[LineItem_sub1.length.positionTo;block=v:line/]" />
<v:line from="[LineItem_sub1.width.positionFrom;block=v:line]" to="[LineItem_sub1.width.positionTo;block=v:line]">
    <v:stroke startarrow="block" endarrow="block"/> <!-- this makes the line into an arrow -->
</v:line>
<v:shape style="position:absolute;left:[LineItem_sub1.positionX;block=v:shape];top:[LineItem_sub1.value;block=v:shape];width:600;height:300;">
   <w:txbxContent>
      <w:t>[LineItem_sub1.value;block=w:t][LineItem_sub1.xstyle;att=v:shape#style]</w:t>
   </w:txbxContent>
</v:shape>


Assuming that positionFrom and positionTo are strings in the specs array like "2000,6800" (x,y).

$LineItem->specs['width']['positionFrom'] = "2000,6800";

So I have the x and y coordinates for the start and finish of each line as well as x,y info for the textbox. There may be multiple dimensions that need to display, but they all have 2 lines and an arrow and a textbox.

I am very interested in the function you made an example of. I tried implementing these snippets you provided, but I don't understand the '.0' you are adding to the [MainBlock.specs.length] - when I add this I get errors: "item before '0' is neither an object nor an array. Its type is string." The same goes for the .xstyle addition. When I throw a var_dump($currRec) into the function, it shows that length is an array (but does not have index 0 or xstyle); however, I also cannot access indexes it does have, by this method [MainBlock.specs.length.value]

I'm sorry I'm making this so complicated, but I would really love to understand how to get this working. Thank you so much for sticking with me.
By: Skrol29
Date: 2012-04-07
Time: 00:33

Re: Automatic Subblocks - no dot operator?

> but I don't understand the '.0' you are adding to the [MainBlock.specs.length]

You said, the data was structured like this:
$lineItem->specs = array('width' => array('value', 'positionX', 'positionY'),
                       'length' => array('value', 'positionX', 'positionY'),
                       'weight' => array('value', 'positionX', 'positionY'));

The code "array('value', 'positionX', 'positionY')" build in fact an array with numerical keys, like this :
array(
  [0] => 'value' ,
  [1] => 'positionX',
  [2] => 'positionY
);'
That's why I've suggested [MainBlock.spec.width.0], where '0' mean for the key 0.

But you may mean that you data are structured like this :
  array('value' => 12345, 'positionX'=>123465, 'positionY'=>123456)

In this case, you must replace [MainBlock.spec.width.0] with [MainBlock.spec.width.value].

> Unfortunately, I don't always have 3 items in my specs array.

So yes, sub-block should be fine.


I can just make another comment on your last DOCX snippet: You don't need to repeat the block parameters on each field of a block. Only once is enough.
That is, you can replace:
<v:line from="[LineItem_sub1.width.positionFrom;block=v:line/]" to="[LineItem_sub1.width.positionTo;block=v:line/]" />
With:
<v:line from="[LineItem_sub1.width.positionFrom;block=v:line/]" to="[LineItem_sub1.width.positionTo]" />


Don't hesitate to ask for help if you're in trouble.

By: Sarah
Date: 2012-04-07
Time: 00:57

Re: Automatic Subblocks - no dot operator?

I had also been unable to use the syntax [MainBlock.spec.width.value]. To elaborate:

Currently when I insert the code you provided (thank you for clarifying!) I receive errors saying that e.g., positionX is not an item in the array.

TinyButStrong Error in field [MainBlock_sub1.length...]: item 'length' is not an existing key in the array.

If I use the [MainBlock_sub1.$] syntax, I get a line where each key is listed (including length), but as soon as I use [MainBlock_sub1.length] I get the error listed. I also get the same error if I use [MainBlock_sub1.length.positionX] or [MainBlock_sub1.length.$]

I feel I am getting very close to solving this, but I am still just missing... something. Thank you for sticking with me.
By: Sarah
Date: 2012-04-10
Time: 19:18

Re: Automatic Subblocks - no dot operator?

I've been spending too much time with my code lately and I restructured a few things when I was unable to get anywhere with my proof of concept. Reading through the forum, I have been wondering if I need to spend more time with $TBS->ObjectRef.

My data structure looks like this:

array(5) {
  [1]=> //There is one w:tbl for each 'Item' - this makes my main block
  object(Item)#5 (9) {
    ["partNum"]=>
    string(5) "12378"
    ["lineNum"]=>
    string(1) "1"
    ["quantity"]=>
    string(2) "50"
    ["info"]=>
    array(8) {
      ["type"]=>
      string(1) "H"
      ["end_type"]=>
      string(1) "S"
    }
    ["specs"]=>
    array(4) {
      ["diameter"]=>
      string(1) "1"
      ["length"]=>
      string(1) "9"
      ["thread"]=>
      string(1) "2"
    }
    ["desc"]=>
    string(67) "String description of item"
    ["image"]=>
    object(Headed)#6 (8) { //Do I need to loop through and add an ObjectRef for each of these?
      ["head"]=>
      object(Image)#7 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(12) "head/hex.png"
        ["image"]=>
        resource(11) of type (gd)
        ["width"]=>
        int(65)
        ["height"]=>
        int(137)
      }
      ["body"]=>
      object(Image)#8 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(13) "body/bolt.png"
        ["image"]=>
        resource(14) of type (gd)
        ["width"]=>
        int(630)
        ["height"]=>
        int(74)
      }
      ["thread"]=>
      object(Image)#9 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(26) "thread/short/rolled-lh.png"
        ["image"]=>
        resource(17) of type (gd)
        ["width"]=>
        int(153)
        ["height"]=>
        int(84)
      }
      ["dimensions"]=> //This is the information I really need. I want to make 'image.dimensions' my sub-block but I cannot use the dot operator in the sub1=image.dimensions and I only get one result if I make 'image' alone my sub-block
      array(3) { //I took out the other two because they have the same structure as length
        ["length"]=>
        object(Dimension)#10 (4) {
          ["left"]=>
          object(Line)#11 (5) {
            ["fromX"]=>
            int(65)
            ["fromY"]=>
            int(365)
            ["toX"]=>
            int(65)
            ["toY"]=>
            int(445)
            ["arrow"]=>
            bool(false)
          }
          ["right"]=>
          object(Line)#12 (5) {
            ["fromX"]=>
            int(705)
            ["fromY"]=>
            int(365)
            ["toX"]=>
            int(705)
            ["toY"]=>
            int(445)
            ["arrow"]=>
            bool(false)
          }
          ["arrow"]=>
          object(Line)#13 (5) {
            ["fromX"]=>
            int(65)
            ["fromY"]=>
            int(425)
            ["toX"]=>
            int(705)
            ["toY"]=>
            int(425)
            ["arrow"]=>
            bool(true)
          }
          ["textBox"]=>
          object(TextBox)#14 (3) {
            ["x"]=>
            int(430)
            ["y"]=>
            int(392)
            ["value"]=>
            string(1) "9"
          }
        }
      }
      ["path"]=>
      string(17) "templates/images/"
      ["fullname"]=>
      string(45) "templates/images/hex-bolt-short-lh-rolled.png"
      ["filename"]=>
      string(28) "hex-bolt-short-lh-rolled.png"
      ["drawing"]=>
      resource(19) of type (gd)
    }
  }
  [2]=>
  object(Item)#25 (9) { //etc...

In order to stop the errors about objects of type 'Headed' not being valid because tbsdb_Headed_open isn't found, I add a ~ to [Mainblock;sub1=~image;block=w:tbl;] and this loop:

foreach ($quote->items as $item){
    $TBS->ObjectRef[] =& $item->image;
}

...prior to my $TBS->LoadTemplate(); call.

When I try to print [Mainblock_sub1.$;block=w:t], [Mainblock_sub1.dimensions;block=w:t], or [Mainblock_sub1.val;block=w:t] (when mainblock is like [Mainblock;sub1=~image.dimensions;block=w:tbl]) - I get nothing, the w:t block is removed from the result.

If I add an asterisk to my MergeBlock, like $temp = $TBS->MergeBlock('Mainblock,*', $quote->items) and then var_dump $temp, I get:

array(5) {
  [1]=>
  object(Item)#5 (10) {
    ["partNum"]=>
    string(5) "12378"
    ["lineNum"]=>
    string(1) "1"
    ["quantity"]=>
    string(2) "50"
    ["info"]=>
    array(8) {
      ["type"]=>
      string(1) "H"
      ["end_type"]=>
      string(1) "S"
    }
    ["specs"]=>
    array(4) {
      ["diameter"]=>
      string(1) "1"
      ["length"]=>
      string(1) "9"
      ["thread"]=>
      string(1) "2"
      ["thread2"]=>
      string(1) "0"
    }
    ["desc"]=>
    string(67) "String description"
    ["image"]=>
    &object(Headed)#6 (8) {
      ["head"]=>
      object(Image)#7 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(12) "head/hex.png"
        ["image"]=>
        resource(11) of type (gd)
        ["width"]=>
        int(65)
        ["height"]=>
        int(137)
      }
      ["body"]=>
      object(Image)#8 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(13) "body/bolt.png"
        ["image"]=>
        resource(14) of type (gd)
        ["width"]=>
        int(630)
        ["height"]=>
        int(74)
      }
      ["thread"]=>
      object(Image)#9 (5) {
        ["path"]=>
        string(17) "templates/images/"
        ["filename"]=>
        string(26) "thread/short/rolled-lh.png"
        ["image"]=>
        resource(17) of type (gd)
        ["width"]=>
        int(153)
        ["height"]=>
        int(84)
      }
      ["dimensions"]=>
      array(3) {
        ["length"]=>
        object(Dimension)#10 (4) {
          ["left"]=>
          object(Line)#11 (5) {
            ["fromX"]=>
            int(65)
            ["fromY"]=>
            int(365)
            ["toX"]=>
            int(65)
            ["toY"]=>
            int(445)
            ["arrow"]=>
            bool(false)
          }
          ["right"]=>
          object(Line)#12 (5) {
            ["fromX"]=>
            int(705)
            ["fromY"]=>
            int(365)
            ["toX"]=>
            int(705)
            ["toY"]=>
            int(445)
            ["arrow"]=>
            bool(false)
          }
          ["arrow"]=>
          object(Line)#13 (5) {
            ["fromX"]=>
            int(65)
            ["fromY"]=>
            int(425)
            ["toX"]=>
            int(705)
            ["toY"]=>
            int(425)
            ["arrow"]=>
            bool(true)
          }
          ["textBox"]=>
          object(TextBox)#14 (3) {
            ["x"]=>
            int(430)
            ["y"]=>
            int(392)
            ["value"]=>
            string(1) "9"
          }
        }
      }
      ["path"]=>
      string(17) "templates/images/"
      ["fullname"]=>
      string(45) "templates/images/hex-bolt-short-lh-rolled.png"
      ["filename"]=>
      string(28) "hex-bolt-short-lh-rolled.png"
      ["drawing"]=>
      resource(19) of type (gd)
    }
    ["~image.dimensions"]=>
    array(0) {
    }
  }
  [2]=>
  object(Item)#25 (10) { //etc

Where ~image (or ~image.dimensions when I use that) is empty. Is it supposed to be empty or can you tell what I am doing wrong that makes it empty?

Do I need to run ObjectRef on each object (Line, Dimensions, Image, Headed, Textbox, and Item)?

Thank you for taking the time to wade through this mess with me. I have been really impressed with how simple TBS with MS Office has been and I'm feeling pretty dumb for not being able to weed out this last detail.
By: Skrol29
Date: 2012-04-11
Time: 00:08

Re: Automatic Subblocks - no dot operator?

Hi Sarah,

> Do I need to run ObjectRef on each object (Line, Dimensions, Image, Headed, Textbox, and Item)?

I don't think so.
ObjectRef is to found values that are stored in local objects rather than global variables.
But put a dictionary of all values in the ObjectRef is not a solution, and I think the sub-block feature cannot handle sub-data sored in a more than one level under a column.

I think the solution is to add a custom column in you data during the merge.

function f_enhance_data($BlockName, &$CurrRec) {
  $CurrRec['dimensions'] =& $CurrRec->image->dimensions;
}
With this function, you just add a column which is a reference to the dimensions array, no duplicated data.

Then you can replace:
[Mainblock;sub1=~image.dimensions;block=w:tbl]
with:
[Mainblock;ondata=f_enhance_data;sub1=dimensions;block=w:tbl]
By: Sarah
Date: 2012-04-11
Time: 00:29

Re: Automatic Subblocks - no dot operator?

I am thrilled, this works perfectly! You are fantastic.

I just changed the $CurrRec['dimensions'] to $CurrRec->dimensions and now I am set!

Thank you so much for seeing through my confusion to the point of the issue and for your very fast replies. TBS is a wonderful library - I never knew how easy it would be to edit create .docx's with PHP.