rbpdf 1.21.1 → 1.21.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +9 -0
  3. data/lib/rbpdf/version.rb +1 -1
  4. data/lib/rbpdf.rb +1528 -328
  5. metadata +2 -2
data/lib/rbpdf.rb CHANGED
@@ -234,7 +234,7 @@ class RBPDF
234
234
  # This is the class constructor.
235
235
  # It allows to set up the page format, the orientation and
236
236
  # the measure unit used in all the methods (except for the font sizes).
237
- # @since 1.0
237
+ # [@since 1.0]
238
238
  # [@param string :orientation]
239
239
  # page orientation. Possible values are (case insensitive):
240
240
  # * P or Portrait (default)
@@ -330,13 +330,16 @@ class RBPDF
330
330
  @listindent ||= 0
331
331
  @listindentlevel ||= 0
332
332
  @lispacer ||= ""
333
+ @li_position_x = nil
333
334
 
334
335
  # bookmark
335
336
  @outlines ||= []
336
337
 
337
338
  # --- javascript and form ---
338
339
  @javascript ||= ''
339
- @js_objects ||= []
340
+ @js_objects ||= {}
341
+ @js_start_obj_id ||= 300000
342
+ @js_obj_id ||= 300000
340
343
 
341
344
  @dpi = 72.0
342
345
  @newpagegroup ||= []
@@ -394,6 +397,7 @@ class RBPDF
394
397
  @page_obj_id ||= []
395
398
  @embedded_start_obj_id ||= 100000
396
399
  @form_obj_id ||= []
400
+ @default_form_prop ||= {'lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>[255, 255, 255], 'strokeColor'=>[128, 128, 128]}
397
401
  @apxo_start_obj_id ||= 400000
398
402
  @apxo_obj_id ||= 400000
399
403
  @annotation_fonts ||= {}
@@ -446,6 +450,7 @@ class RBPDF
446
450
  else
447
451
  @buffer ||= ''
448
452
  end
453
+ @tmp_buffer = nil
449
454
  @pages ||= []
450
455
  @prev_pages ||= []
451
456
  @state ||= 0
@@ -4025,7 +4030,9 @@ class RBPDF
4025
4030
  ccode = getCellCode(w, h, '', border, 1, '', fill, '', 0, true)
4026
4031
  @lasth = prevLastH
4027
4032
 
4028
- if (border != 0) or (fill == 1)
4033
+ if @tmp_buffer
4034
+ @tmp_buffer = "#{ccode}\n#{@tmp_buffer}"
4035
+ elsif (border != 0) or (fill == 1)
4029
4036
  if !@transfmrk[@page].nil?
4030
4037
  pagemark = @transfmrk[@page]
4031
4038
  @transfmrk[@page] += (ccode + "\n").length
@@ -6033,21 +6040,20 @@ protected
6033
6040
  annots << ' /FT /Btn'
6034
6041
  annots << ' /Ff 49152'
6035
6042
  annots << ' /Kids ['
6043
+ defval = nil
6036
6044
  @radiobutton_groups[n][pl['txt']].each {|data|
6037
- annots << ' ' + data['kid'] + ' 0 R'
6045
+ annots << " #{data['kid']} 0 R"
6038
6046
  if data['def'] != 'Off'
6039
6047
  defval = data['def']
6040
6048
  end
6041
6049
  }
6042
6050
  annots << ' ]'
6043
- if defval
6044
- annots << ' /V /' + defval
6045
- end
6051
+ annots << " /V /#{defval}" if defval
6046
6052
  annots << ' >>'
6047
6053
  @annot_obj_id += 1
6048
6054
  @offsets[@annot_obj_id] = @bufferlen
6049
- out(@annot_obj_id + ' 0 obj ' + annots + ' endobj')
6050
- @form_obj_id.push = @annot_obj_id
6055
+ out("#{@annot_obj_id} 0 obj #{annots} endobj")
6056
+ @form_obj_id << @annot_obj_id
6051
6057
  # store object id to be used on Parent entry of Kids
6052
6058
  @radiobutton_groups[n][pl['txt']] = @annot_obj_id
6053
6059
  end
@@ -6105,7 +6111,7 @@ protected
6105
6111
  else
6106
6112
  val = pl['opt']['f'].to_i
6107
6113
  end
6108
- annots << ' /F ' + val.to_i
6114
+ annots << " /F #{val}"
6109
6115
  end
6110
6116
  # annots << ' /AP '
6111
6117
  # annots << ' /AS '
@@ -6114,24 +6120,23 @@ protected
6114
6120
  end
6115
6121
  if pl['opt']['ap']
6116
6122
  # appearance stream
6117
- annots << ' /AP << ' + pl['opt']['ap'] + ' >>'
6118
6123
  annots << ' /AP <<'
6119
6124
  if pl['opt']['ap'].is_a?(Hash)
6120
6125
  pl['opt']['ap'].each {|apmode, apdef|
6121
6126
  # apmode can be: n = normal; r = rollover; d = down
6122
6127
  annots << ' /' + apmode.upcase
6123
- if apdef.is_a?(Array)
6128
+ if apdef.is_a?(Hash)
6124
6129
  annots << ' <<'
6125
6130
  apdef.each {|apstate, stream|
6126
6131
  # reference to XObject that define the appearance for this mode-state
6127
6132
  apsobjid = putAPXObject(c, d, stream)
6128
- annots << ' /' + apstate + ' ' + apsobjid + ' 0 R'
6133
+ annots << " /#{apstate} #{apsobjid} 0 R"
6129
6134
  }
6130
6135
  annots << ' >>'
6131
6136
  else
6132
6137
  # reference to XObject that define the appearance for this mode
6133
6138
  apsobjid = putAPXObject(c, d, apdef)
6134
- annots << ' ' + apsobjid + ' 0 R'
6139
+ annots << " #{apsobjid} 0 R"
6135
6140
  end
6136
6141
  }
6137
6142
  else
@@ -6143,7 +6148,7 @@ protected
6143
6148
  annots << ' /BS <<'
6144
6149
  annots << ' /Type /Border'
6145
6150
  if !pl['opt']['bs']['w'].nil?
6146
- annots << ' /W ' + pl['opt']['bs']['w'].to_i
6151
+ annots << " /W #{pl['opt']['bs']['w']}"
6147
6152
  end
6148
6153
  bstyles = ['S', 'D', 'B', 'I', 'U']
6149
6154
  if !pl['opt']['bs']['s'].nil? and bstyles.include?(pl['opt']['bs']['s'])
@@ -6205,7 +6210,7 @@ protected
6205
6210
  if !pl['opt']['t'].nil? and pl['opt']['t'].is_a?(String)
6206
6211
  annots << ' /T ' + textannobjstring(pl['opt']['t'])
6207
6212
  end
6208
- # annots .= ' /Popup '
6213
+ # annots << ' /Popup '
6209
6214
  if !pl['opt']['ca'].nil?
6210
6215
  annots << ' /CA ' + sprintf("%.4f", pl['opt']['ca'].to_f)
6211
6216
  end
@@ -6420,9 +6425,9 @@ protected
6420
6425
  end # end MK
6421
6426
 
6422
6427
  # --- Entries for field dictionaries ---
6423
- if @radiobutton_groups[n][pl['txt']]
6428
+ if @radiobutton_groups[n] and @radiobutton_groups[n][pl['txt']]
6424
6429
  # set parent
6425
- annots << ' /Parent ' + @radiobutton_groups[n][pl['txt']] + ' 0 R'
6430
+ annots << " /Parent #{@radiobutton_groups[n][pl['txt']]} 0 R"
6426
6431
  end
6427
6432
  if pl['opt']['t'] and pl['opt']['t'].is_a?(String)
6428
6433
  annots << ' /T ' + dataannobjstring(pl['opt']['t'])
@@ -6443,7 +6448,7 @@ protected
6443
6448
  else
6444
6449
  flag = pl['opt']['ff'].to_i
6445
6450
  end
6446
- annots << ' /Ff ' + flag
6451
+ annots << " /Ff #{flag}"
6447
6452
  end
6448
6453
  if pl['opt']['maxlen']
6449
6454
  annots << ' /MaxLen ' + pl['opt']['maxlen'].to_i.to_s
@@ -6529,9 +6534,9 @@ protected
6529
6534
  @offsets[@annot_obj_id] = @bufferlen
6530
6535
  out(@annot_obj_id.to_s + ' 0 obj ' + annots + ' endobj')
6531
6536
 
6532
- if formfield and ! @radiobutton_groups[n][pl['txt']]
6537
+ if formfield and !(@radiobutton_groups[n] and @radiobutton_groups[n][pl['txt']])
6533
6538
  # store reference of form object
6534
- @form_obj_id.push = @annot_obj_id
6539
+ @form_obj_id << @annot_obj_id
6535
6540
  end
6536
6541
  }
6537
6542
  end # end for each page
@@ -6563,7 +6568,7 @@ protected
6563
6568
  rect = sprintf('%.2f %.2f', w, h)
6564
6569
  out << ' /BBox [0 0 ' + rect + ']'
6565
6570
  out << ' /Matrix [1 0 0 1 0 0]'
6566
- out << ' /Resources <</ProcSet [/PDF]>>'
6571
+ out << ' /Resources 2 0 R'
6567
6572
  out << ' /Length ' + stream.length.to_s
6568
6573
  out << ' >>'
6569
6574
  out << ' ' + getstream(stream)
@@ -7163,7 +7168,7 @@ protected
7163
7168
  out(out)
7164
7169
  end
7165
7170
  end
7166
- @fontkeys.each do |k|
7171
+ @fontkeys.each_with_index do |k, i|
7167
7172
  #Font objects
7168
7173
  setFontSubBuffer(k, 'n', @n + 1)
7169
7174
  font = getFontBuffer(k)
@@ -7181,7 +7186,7 @@ protected
7181
7186
  end
7182
7187
  if name.downcase == 'helvetica'
7183
7188
  # add default font for annotations
7184
- @annotation_fonts['helvetica'] = k
7189
+ @annotation_fonts['helvetica'] = i
7185
7190
  end
7186
7191
  out << ' >> endobj'
7187
7192
  out(out)
@@ -7232,9 +7237,9 @@ protected
7232
7237
  Error('Unsupported font type: ' + type)
7233
7238
  end
7234
7239
  obj_id = self.send(mtd,font)
7235
- # store object ID for current font
7236
- @font_obj_ids[k] = obj_id
7237
7240
  end
7241
+ # store object ID for current font
7242
+ @font_obj_ids[k] = obj_id
7238
7243
  end
7239
7244
  end
7240
7245
 
@@ -7595,6 +7600,7 @@ protected
7595
7600
  putbookmarks()
7596
7601
  putEmbeddedFiles()
7597
7602
  putannotsobjs()
7603
+ putjavascript()
7598
7604
  # encryption
7599
7605
 
7600
7606
  ### T.B.D ### TCPDF 5.0.000 ###
@@ -7653,11 +7659,11 @@ protected
7653
7659
  newobj()
7654
7660
  out = '<< /Type /Catalog'
7655
7661
  out << ' /Pages 1 0 R'
7656
- if (@zoom_mode=='fullpage')
7662
+ if @zoom_mode == 'fullpage'
7657
7663
  out << ' /OpenAction [3 0 R /Fit]'
7658
- elsif (@zoom_mode=='fullwidth')
7664
+ elsif @zoom_mode == 'fullwidth'
7659
7665
  out << ' /OpenAction [3 0 R /FitH null]'
7660
- elsif (@zoom_mode=='real')
7666
+ elsif @zoom_mode == 'real'
7661
7667
  out << ' /OpenAction [3 0 R /XYZ null null 1]'
7662
7668
  elsif @zoom_mode.is_a?(Numeric)
7663
7669
  out << ' /OpenAction [3 0 R /XYZ null null ' + (@zoom_mode/100.0).to_s + ']'
@@ -7674,7 +7680,7 @@ protected
7674
7680
  end
7675
7681
  out << ' /Names <<'
7676
7682
  if !@javascript.empty? or !@js_objects.empty?
7677
- out << ' /JavaScript ' + @n_js + ' 0 R'
7683
+ out << " /JavaScript #{@n_js} 0 R"
7678
7684
  end
7679
7685
  out << ' >>'
7680
7686
 
@@ -7687,9 +7693,43 @@ protected
7687
7693
  v = @n_ocg_view.to_s + ' 0 R'
7688
7694
  as = '<</Event /Print /OCGs [' + p + ' ' + v + '] /Category [/Print]>> <</Event /View /OCGs [' + p + ' ' + v + '] /Category [/View]>>'
7689
7695
  out << ' /OCProperties <</OCGs [' + p + ' ' + v + '] /D <</ON [' + p + '] /OFF [' + v + '] /AS [' + as + ']>>>>'
7696
+ # AcroForm
7697
+ if !@form_obj_id.empty? || (@sign && @signature_data['cert_type'])
7698
+ out << ' /AcroForm<<'
7699
+ objrefs = ''
7700
+ if @sign && @signature_data['cert_type']
7701
+ objrefs << "#{@sig_obj_id} 0 R"
7702
+ end
7703
+ @form_obj_id.each{|objid|
7704
+ objrefs << " #{objid} 0 R"
7705
+ }
7706
+ out << " /Fields [#{objrefs}]"
7707
+ if @sign && @signature_data['cert_type']
7708
+ out << ' /SigFlags 3'
7709
+ end
7690
7710
 
7691
- ### T.B.D ### TCPDF 5.0.000 ###
7692
-
7711
+ #out << ' /CO '
7712
+ unless @annotation_fonts.empty?
7713
+ out << ' /DR <<'
7714
+ out << ' /Font <<'
7715
+ @annotation_fonts.each {|font, fontkey|
7716
+ out << " /F#{fontkey + 1} #{@font_obj_ids[font]} 0 R"
7717
+ }
7718
+ out << ' >> >>'
7719
+ end
7720
+ out << " /DA (/F#{@fontkeys.index('helvetica') + 1} 0 Tf 0 g)"
7721
+ out << ' /Q ' + (@rtl ? '2' : '0')
7722
+ #out << ' /XFA '
7723
+ out << ' >>'
7724
+ # signatures
7725
+ if @sign && @signature_data['cert_type']
7726
+ if @signature_data['cert_type'] > 0
7727
+ out << " /Perms<</DocMDP #{@sig_obj_id + 1} 0 R>>"
7728
+ else
7729
+ out << " /Perms<</UR3 #{@sig_obj_id + 1} 0 R>>"
7730
+ end
7731
+ end
7732
+ end
7693
7733
  out << ' >> endobj'
7694
7734
  out(out)
7695
7735
  end
@@ -7833,6 +7873,20 @@ protected
7833
7873
  out(sprintf('%010d 00000 n ', @offsets[i]))
7834
7874
  end
7835
7875
  end
7876
+ # Javascript Objects
7877
+ if @js_obj_id > @js_start_obj_id
7878
+ out((@js_start_obj_id + 1).to_s + ' ' + (@js_obj_id - @js_start_obj_id).to_s)
7879
+ (@js_start_obj_id + 1).upto(@js_obj_id) do |i|
7880
+ out(sprintf('%010d 00000 n ', @offsets[i]))
7881
+ end
7882
+ end
7883
+ # Appearance streams XObjects
7884
+ if @apxo_obj_id > @apxo_start_obj_id
7885
+ out((@apxo_start_obj_id + 1).to_s + ' ' + (@apxo_obj_id - @apxo_start_obj_id).to_s)
7886
+ (@apxo_start_obj_id + 1).upto(@apxo_obj_id) do |i|
7887
+ out(sprintf('%010d 00000 n ', @offsets[i]))
7888
+ end
7889
+ end
7836
7890
  #Trailer
7837
7891
  puttrailer();
7838
7892
  out('startxref');
@@ -8108,9 +8162,9 @@ protected
8108
8162
 
8109
8163
  #
8110
8164
  # get raw output stream.
8111
- # @param string :s string to output.
8112
- # @param int :n object reference for encryption mode
8113
- # @access protected
8165
+ # [@param string :s] string to output.
8166
+ # [@param int :n] object reference for encryption mode
8167
+ # [@access protected]
8114
8168
  #
8115
8169
  def getrawstream(s, n=0)
8116
8170
  if n <= 0
@@ -8122,9 +8176,9 @@ protected
8122
8176
 
8123
8177
  #
8124
8178
  # Format output stream
8125
- # @param string :s string to output.
8126
- # @param int :n object reference for encryption mode
8127
- # @access protected
8179
+ # [@param string :s] string to output.
8180
+ # [@param int :n] object reference for encryption mode
8181
+ # [@access protected]
8128
8182
  #
8129
8183
  def getstream(s, n=0)
8130
8184
  "stream\n" + getrawstream(s, n=0) + "\nendstream"
@@ -8147,7 +8201,10 @@ protected
8147
8201
  #
8148
8202
  def out(s)
8149
8203
  s.force_encoding('ASCII-8BIT') if s.respond_to?(:force_encoding)
8150
- if (@state==2)
8204
+
8205
+ if @tmp_buffer
8206
+ @tmp_buffer << "#{s}\n"
8207
+ elsif (@state==2)
8151
8208
  if !@in_footer and !@footerlen[@page].nil? and (@footerlen[@page] > 0)
8152
8209
  # puts data before page footer
8153
8210
  pagebuff = getPageBuffer(@page)
@@ -10317,7 +10374,7 @@ public
10317
10374
  return
10318
10375
  end
10319
10376
  # sort outlines by page and original position
10320
- @outlines = @outlines.sort_by {|x| x[:p]}
10377
+ @outlines = @outlines.sort_by {|x| [x[:p], x[:y]] }
10321
10378
  lru = []
10322
10379
  level = 0
10323
10380
  @outlines.each_with_index do |o, i|
@@ -10370,7 +10427,924 @@ public
10370
10427
  protected :putbookmarks
10371
10428
 
10372
10429
  # --- JAVASCRIPT ------------------------------------------------------
10430
+ #
10431
+ # Adds a javascript
10432
+ # [@param string :script] Javascript code
10433
+ # [@access public]
10434
+ # [@author Johannes G�ntert, Nicola Asuni]
10435
+ # [@since 2.1.002 (2008-02-12)]
10436
+ #
10437
+ def IncludeJS(script)
10438
+ @javascript << script
10439
+ end
10440
+
10441
+ #
10442
+ # Adds a javascript object and return object ID
10443
+ # [@param string :script] Javascript code
10444
+ # [@param boolean :onload] if true executes this object when opening the document
10445
+ # [@return int] internal object ID
10446
+ # [@access public]
10447
+ # [@author Nicola Asuni]
10448
+ # [@since 4.8.000 (2009-09-07)]
10449
+ #
10450
+ def addJavascriptObject(script, onload = false)
10451
+ @js_obj_id += 1
10452
+ @js_objects[@js_obj_id] = {'js' => script, 'onload' => onload}
10453
+ @js_obj_id
10454
+ end
10455
+
10456
+ #
10457
+ # Create a javascript PDF string.
10458
+ # [@access protected]
10459
+ # [@author Johannes G�ntert, Nicola Asuni]
10460
+ # [@since 2.1.002 (2008-02-12)]
10461
+ #
10462
+ def putjavascript()
10463
+ return if @javascript.empty? && @js_objects.empty?
10464
+
10465
+ if @javascript.index 'this.addField'
10466
+ # @setUserRights() unless @ur
10467
+
10468
+ # the following two lines are used to avoid form fields duplication after saving
10469
+ # The addField method only works on Acrobat Writer, unless the document is signed with Adobe private key (UR3)
10470
+ jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1)
10471
+ jsb = "getField('tcpdfdocsaved').value='saved';"
10472
+ @javascript = "#{jsa}\n#{@javascript}\n#{jsb}"
10473
+ end
10474
+ @n_js = newobj()
10475
+ out = ' << /Names ['
10476
+ unless @javascript.empty?
10477
+ out << " (EmbeddedJS) #{@n + 1} 0 R"
10478
+ end
10479
+ unless @js_objects.empty?
10480
+ @js_objects.each{|key, val|
10481
+ out << " (JS#{key}) #{key} 0 R" if val['onload']
10482
+ }
10483
+ end
10484
+ out << ' ] >> endobj'
10485
+ out(out)
10486
+ # default Javascript object
10487
+ unless @javascript.empty?
10488
+ newobj()
10489
+ out = '<< /S /JavaScript'
10490
+ out << " /JS #{textstring(@javascript)}"
10491
+ out << ' >> endobj'
10492
+ out(out)
10493
+ end
10494
+ # additional Javascript objects
10495
+ unless @js_objects.empty?
10496
+ @js_objects.each {|key, val|
10497
+ @offsets[key] = @bufferlen
10498
+ out = "#{key} 0 obj\n << /S /JavaScript /JS #{textstring(val['js'])} >> endobj"
10499
+ out(out)
10500
+ }
10501
+ end
10502
+ end
10503
+
10504
+ #
10505
+ # Convert color to javascript color.
10506
+ # [@param string :color] color name or #RRGGBB
10507
+ # [@access protected}
10508
+ # [@author Denis Van Nuffelen, Nicola Asuni]
10509
+ # [@since 2.1.002 (2008-02-12)]
10510
+ #
10511
+ def JScolor(color)
10512
+ aColors = ['transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray']
10513
+ if color[0] == '#'
10514
+ return sprintf("['RGB',%.3f,%.3f,%.3f]", color[1, 2].to_i(16) / 255, color[3, 2].to_i(16) / 255, color[5, 2].to_i(16) / 255)
10515
+ end
10516
+
10517
+ unless aColors.include? color
10518
+ Error('Invalid color: ' + color)
10519
+ end
10520
+ 'color.' + color
10521
+ end
10522
+ protected :JScolor
10523
+
10524
+ #
10525
+ # Adds a javascript form field.
10526
+ # [@param string :type] field type
10527
+ # [@param string :name] field name
10528
+ # [@param int :x] horizontal position
10529
+ # [@param int :y] vertical position
10530
+ # [@param int :w] width
10531
+ # [@param int :h] height
10532
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10533
+ # [@access protected]
10534
+ # [@author Denis Van Nuffelen, Nicola Asuni]
10535
+ # [@since 2.1.002 (2008-02-12)]
10536
+ #
10537
+ def addfield(type, name, x, y, w, h, prop)
10538
+ x = x - w if @rtl
10539
+
10540
+ # the followind avoid fields duplication after saving the document
10541
+ @javascript << "if(getField('tcpdfdocsaved').value != 'saved') {"
10542
+ k = @k
10543
+ @javascript << "f#{name}=this.addField('#{name}','#{type}',#{PageNo() - 1},[#{sprintf("%.2f,%.2f,%.2f,%.2f", x * k, (@h - y) * k + 1, (x + w) * k, (@h - y - h) * k + 1)}]);\n"
10544
+ @javascript << "f#{name}.textSize=#{@font_size_pt};\n"
10545
+ prop.each {|k, v|
10546
+ val = k[-5..-1] == 'Color' ? JScolor(v) : "'#{v}'"
10547
+ @javascript << "f#{name}.#{k}=#{val};\n"
10548
+ }
10549
+ if @rtl
10550
+ @x -= w
10551
+ else
10552
+ @x += w
10553
+ end
10554
+ @javascript << '}'
10555
+ end
10556
+ protected :addfield
10557
+
10373
10558
  # --- FORM FIELDS -----------------------------------------------------
10559
+
10560
+ #
10561
+ # Convert JavaScript form fields properties array to Annotation Properties array.
10562
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10563
+ # [@return array of annotation properties]
10564
+ # [@access protected]
10565
+ # [@author Nicola Asuni]
10566
+ # [@since 4.8.000 (2009-09-06)]
10567
+ #
10568
+ def getAnnotOptFromJSProp(prop)
10569
+ # the annotation options area already defined
10570
+ return prop['aopt'] if prop['aopt'].is_a?(Array)
10571
+
10572
+ opt = {} # value to be returned
10573
+ # alignment: Controls how the text is laid out within the text field.
10574
+ if prop['alignment']
10575
+ opt['q'] = case prop['alignment']
10576
+ when 'left'; 0
10577
+ when 'center'; 1
10578
+ when 'right'; 2
10579
+ else @rtl ? 2 : 0
10580
+ end
10581
+ end
10582
+ # lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
10583
+ if prop['lineWidth']
10584
+ linewidth = prop['lineWidth'].to_i
10585
+ else
10586
+ linewidth = 1
10587
+ end
10588
+ # borderStyle: The border style for a field.
10589
+ case prop['borderStyle']
10590
+ when 'border.d', 'dashed'
10591
+ opt['border'] = [0, 0, linewidth, [3, 2]]
10592
+ opt['bs'] = {'w'=>linewidth, 's'=>'D', 'd'=>[3, 2]}
10593
+ when 'border.b', 'beveled'
10594
+ opt['border'] = [0, 0, linewidth]
10595
+ opt['bs'] = {'w'=>linewidth, 's'=>'B'}
10596
+ when 'border.i', 'inset'
10597
+ opt['border'] = [0, 0, linewidth]
10598
+ opt['bs'] = {'w'=>linewidth, 's'=>'I'}
10599
+ when 'border.u', 'underline'
10600
+ opt['border'] = [0, 0, linewidth]
10601
+ opt['bs'] = {'w'=>linewidth, 's'=>'U'}
10602
+ else # 'border.s', 'solid'
10603
+ opt['border'] = [0, 0, linewidth]
10604
+ opt['bs'] = {'w'=>linewidth, 's'=>'S'}
10605
+ end
10606
+
10607
+ opt['border'] = prop['border'] if prop['border'].is_a?(Array)
10608
+ opt['mk'] ||= {}
10609
+ opt['mk']['if'] ||= {}
10610
+ opt['mk']['if']['a'] = [0.5, 0.5]
10611
+ # buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
10612
+ opt['mk']['if']['a'][0] = prop['buttonAlignX'] if prop['buttonAlignX']
10613
+ # buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
10614
+ opt['mk']['if']['a'][1] = prop['buttonAlignY'] if prop['buttonAlignY']
10615
+ # buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
10616
+ opt['mk']['if']['fb'] = true if prop['buttonFitBounds'] && (prop['buttonFitBounds'] == 'true')
10617
+ # buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
10618
+ case prop['buttonScaleHow']
10619
+ when 'scaleHow.proportional'; opt['mk']['if']['s'] = 'P'
10620
+ when 'scaleHow.anamorphic'; opt['mk']['if']['s'] = 'A'
10621
+ end
10622
+ # buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
10623
+ case prop['buttonScaleWhen']
10624
+ when 'scaleWhen.always'; opt['mk']['if']['sw'] = 'A'
10625
+ when 'scaleWhen.never'; opt['mk']['if']['sw'] = 'N'
10626
+ when 'scaleWhen.tooBig'; opt['mk']['if']['sw'] = 'B'
10627
+ when 'scaleWhen.tooSmall'; opt['mk']['if']['sw'] = 'S'
10628
+ end
10629
+ # buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
10630
+ case prop['buttonPosition']
10631
+ when 0, 'position.textOnly'; opt['mk']['tp'] = 0
10632
+ when 1, 'position.iconOnly'; opt['mk']['tp'] = 1
10633
+ when 2, 'position.iconTextV'; opt['mk']['tp'] = 2
10634
+ when 3, 'position.textIconV'; opt['mk']['tp'] = 3
10635
+ when 4, 'position.iconTextH'; opt['mk']['tp'] = 4
10636
+ when 5, 'position.textIconH'; opt['mk']['tp'] = 5
10637
+ when 6, 'position.overlay'; opt['mk']['tp'] = 6
10638
+ end
10639
+ # fillColor: Specifies the background color for a field.
10640
+ if prop['fillColor']
10641
+ if prop['fillColor'].is_a? Array
10642
+ opt['mk']['bg'] = prop['fillColor']
10643
+ else
10644
+ opt['mk']['bg'] = convertHTMLColorToDec(prop['fillColor'])
10645
+ end
10646
+ end
10647
+ # strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
10648
+ if prop['strokeColor']
10649
+ if prop['strokeColor'].is_a? Array
10650
+ opt['mk']['bc'] = prop['strokeColor']
10651
+ else
10652
+ opt['mk']['bc'] = convertHTMLColorToDec(prop['strokeColor'])
10653
+ end
10654
+ end
10655
+ # rotation: The rotation of a widget in counterclockwise increments.
10656
+ opt['mk']['r'] = prop['rotation'] if prop['rotation']
10657
+ # charLimit: Limits the number of characters that a user can type into a text field.
10658
+ opt['maxlen'] = prop['charLimit'].to_i if prop['charLimit']
10659
+
10660
+ ff ||= 0
10661
+ # readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10662
+ ff |= 1 << 0 if prop['readonly'] == 'true'
10663
+ # required: Specifies whether a field requires a value.
10664
+ ff |= 1 << 1 if prop['required'] == 'true'
10665
+ # multiline: Controls how text is wrapped within the field.
10666
+ ff |= 1 << 12 if prop['multiline'] == 'true'
10667
+ # password: Specifies whether the field should display asterisks when data is entered in the field.
10668
+ ff |= 1 << 13 if prop['password'] == 'true'
10669
+ # NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
10670
+ ff |= 1 << 14 if prop['NoToggleToOff'] == 'true'
10671
+ # Radio: If set, the field is a set of radio buttons.
10672
+ ff |= 1 << 15 if prop['Radio'] == 'true'
10673
+ # Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
10674
+ ff |= 1 << 16 if prop['Pushbutton'] == 'true'
10675
+ # Combo: If set, the field is a combo box; if clear, the field is a list box.
10676
+ ff |= 1 << 17 if prop['Combo'] == 'true'
10677
+ # editable: Controls whether a combo box is editable.
10678
+ ff |= 1 << 18 if prop['editable'] == 'true'
10679
+ # Sort: If set, the field's option items shall be sorted alphabetically.
10680
+ ff |= 1 << 19 if prop['Sort'] == 'true'
10681
+ # fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
10682
+ ff |= 1 << 20 if prop['fileSelect'] == 'true'
10683
+ # multipleSelection: If true, indicates that a list box allows a multiple selection of items.
10684
+ ff |= 1 << 21 if prop['multipleSelection'] == 'true'
10685
+ # doNotSpellCheck: If true, spell checking is not performed on this editable text field.
10686
+ ff |= 1 << 22 if prop['doNotSpellCheck'] == 'true'
10687
+ # doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
10688
+ ff |= 1 << 23 if prop['doNotScroll'] == 'true'
10689
+ # comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
10690
+ ff |= 1 << 24 if prop['comb'] == 'true'
10691
+ # radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
10692
+ ff |= 1 << 25 if prop['radiosInUnison'] == 'true'
10693
+ # richText: If true, the field allows rich text formatting.
10694
+ ff |= 1 << 25 if prop['richText'] == 'true'
10695
+ # commitOnSelChange: Controls whether a field value is committed after a selection change.
10696
+ ff |= 1 << 26 if prop['commitOnSelChange'] == 'true'
10697
+ opt['ff'] = ff
10698
+
10699
+ # defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
10700
+ opt['dv'] = prop['defaultValue'] if prop['defaultValue']
10701
+ f = 1 << 2 # default value for annotation flags
10702
+ # readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
10703
+ if prop['readonly'] == 'true'
10704
+ f |= 1 << 6
10705
+ end
10706
+ # display: Controls whether the field is hidden or visible on screen and in print.
10707
+ case prop['display']
10708
+ when 'display.visible'
10709
+ #
10710
+ when 'display.hidden'
10711
+ f |= 1 << 1
10712
+ when 'display.noPrint'
10713
+ f &= ~(1 << 2)
10714
+ when 'display.noView'
10715
+ f |= 1 << 5
10716
+ end
10717
+ opt['f'] = f
10718
+
10719
+ # currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
10720
+ opt['i'] = prop['currentValueIndices'] if prop['currentValueIndices'].is_a?(Array)
10721
+ # value: The value of the field data that the user has entered.
10722
+ if prop['value']
10723
+ if prop['value'].is_a?(Array)
10724
+ opt['opt'] = []
10725
+ prop['value'].each_with_index {|v, i|
10726
+ # exportValues: An array of strings representing the export values for the field.
10727
+ if prop['exportValues'] && prop['exportValues'][i]
10728
+ opt['opt'][i] = [prop['exportValues'][i], v]
10729
+ else
10730
+ opt['opt'][i] = v
10731
+ end
10732
+ }
10733
+ else
10734
+ opt['v'] = prop['value']
10735
+ end
10736
+ end
10737
+ # richValue: This property specifies the text contents and formatting of a rich text field.
10738
+ opt['rv'] = prop['richValue'] if prop['richValue']
10739
+ # submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
10740
+ opt['tm'] = prop['submitName'] if prop['submitName']
10741
+ # name: Fully qualified field name.
10742
+ opt['t'] = prop['name'] if prop['name']
10743
+ # userName: The user name (short description string) of the field.
10744
+ opt['tu'] = prop['userName'] if prop['userName']
10745
+ # highlight: Defines how a button reacts when a user clicks it.
10746
+ case prop['highlight']
10747
+ when 'none', 'highlight.n'; opt['h'] = 'N'
10748
+ when 'invert', 'highlight.i'; opt['h'] = 'i'
10749
+ when 'push', 'highlight.p'; opt['h'] = 'P'
10750
+ when 'outline', 'highlight.o'; opt['h'] = 'O'
10751
+ end
10752
+ # Unsupported options:
10753
+ # - calcOrderIndex: Changes the calculation order of fields in the document.
10754
+ # - delay: Delays the redrawing of a field's appearance.
10755
+ # - defaultStyle: This property defines the default style attributes for the form field.
10756
+ # - style: Allows the user to set the glyph style of a check box or radio button.
10757
+ # - textColor, textFont, textSize
10758
+ opt
10759
+ end
10760
+ protected :getAnnotOptFromJSProp
10761
+
10762
+ #
10763
+ # Set default properties for form fields.
10764
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10765
+ # [@access public]
10766
+ # [@author Nicola Asuni]
10767
+ # [@since 4.8.000 (2009-09-06)]
10768
+ #
10769
+ def setFormDefaultProp(prop = [])
10770
+ @default_form_prop = prop
10771
+ end
10772
+ alias_method :set_form_default_prop, :setFormDefaultProp
10773
+
10774
+ #
10775
+ # Return the default properties for form fields.
10776
+ # [@return array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10777
+ # [@access public]
10778
+ # [@author Nicola Asuni]
10779
+ # [@since 4.8.000 (2009-09-06)]
10780
+ #
10781
+ def getFormDefaultProp()
10782
+ @default_form_prop
10783
+ end
10784
+ alias_method :get_form_default_prop, :getFormDefaultProp
10785
+
10786
+ #
10787
+ # Creates a text field
10788
+ # [@param string :name] field name
10789
+ # [@param float :w] Width of the rectangle
10790
+ # [@param float :h] Height of the rectangle
10791
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10792
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10793
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10794
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10795
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10796
+ # [@access public]
10797
+ # [@author Nicola Asuni]
10798
+ # [@since 4.8.000 (2009-09-07)]
10799
+ #
10800
+ def TextField(name, w, h, prop = {}, opt = {}, x = '', y = '', js = false)
10801
+ x = @x if x == ''
10802
+ y = @y if y == ''
10803
+
10804
+ if js
10805
+ addfield('text', name, x, y, w, h, prop)
10806
+ return
10807
+ end
10808
+ # get default style
10809
+ prop = getFormDefaultProp.merge prop
10810
+ # get annotation data
10811
+ popt = getAnnotOptFromJSProp(prop)
10812
+ # set default appearance stream
10813
+ font = @font_family
10814
+ fontkey = @fontkeys.index font
10815
+ unless @annotation_fonts.include? fontkey
10816
+ @annotation_fonts[font] = fontkey
10817
+ end
10818
+ fontstyle = sprintf("/F%d %.2f Tf %s", fontkey + 1, @font_size_pt, @text_color)
10819
+ popt['da'] = fontstyle
10820
+ popt['ap'] = {}
10821
+
10822
+ if opt['v'] && !empty_string(opt['v'])
10823
+ # set Appearances
10824
+ popt['ap']['n'] = "/Tx BMC q #{fontstyle} "
10825
+ gvars = getGraphicVars()
10826
+ @h = h
10827
+ @w = w
10828
+ @t_margin = 0
10829
+ @c_margin = 0.2
10830
+
10831
+ @tmp_buffer = ''
10832
+ multi_cell(w, h, opt['v'], 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
10833
+ popt['ap']['n'] << @tmp_buffer
10834
+ @tmp_buffer = nil
10835
+ popt['ap']['n'] << 'Q EMC'
10836
+
10837
+ # restore previous values
10838
+ setGraphicVars(gvars, true)
10839
+ else
10840
+ popt['ap']['n'] = "q BT #{fontstyle} ET Q"
10841
+ end
10842
+
10843
+ # merge options
10844
+ opt = popt.merge opt
10845
+ # remove some conflicting options
10846
+ opt.delete :bs
10847
+ # set remaining annotation data
10848
+ opt['Subtype'] = 'Widget'
10849
+ opt['ft'] = 'Tx'
10850
+ opt['t'] = name
10851
+ #
10852
+ # Additional annotation's parameters (check _putannotsobj() method):
10853
+ # opt['f']
10854
+ # opt['ap']
10855
+ # opt['as']
10856
+ # opt['bs']
10857
+ # opt['be']
10858
+ # opt['c']
10859
+ # opt['border']
10860
+ # opt['h']
10861
+ # opt['mk']
10862
+ # opt['mk']['r']
10863
+ # opt['mk']['bc']
10864
+ # opt['mk']['bg']
10865
+ # opt['mk']['ca']
10866
+ # opt['mk']['rc']
10867
+ # opt['mk']['ac']
10868
+ # opt['mk']['i']
10869
+ # opt['mk']['ri']
10870
+ # opt['mk']['ix']
10871
+ # opt['mk']['if']
10872
+ # opt['mk']['if']['sw']
10873
+ # opt['mk']['if']['s']
10874
+ # opt['mk']['if']['a']
10875
+ # opt['mk']['if']['fb']
10876
+ # opt['mk']['tp']
10877
+ # opt['tu']
10878
+ # opt['tm']
10879
+ # opt['ff']
10880
+ # opt['v']
10881
+ # opt['dv']
10882
+ # opt['a']
10883
+ # opt['aa']
10884
+ # opt['q']
10885
+ Annotation(x, y, w, h, name, opt, 0)
10886
+ if @rtl
10887
+ @x -= w
10888
+ else
10889
+ @x += w
10890
+ end
10891
+ end
10892
+ alias_method :text_field, :TextField
10893
+
10894
+ #
10895
+ # Creates a RadioButton field
10896
+ # [@param string :name] field name
10897
+ # [@param int :w] width
10898
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10899
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10900
+ # [@param string :onvalue] value to be returned if selected.
10901
+ # [@param boolean :checked] define the initial state.
10902
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10903
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10904
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10905
+ # [@access public]
10906
+ # [@author Nicola Asuni]
10907
+ # [@since 4.8.000 (2009-09-07)]
10908
+ #
10909
+ def RadioButton(name, w, prop = {}, opt = {}, onvalue = 'On', checked = false, x = '', y = '', js = false)
10910
+ x = @x if x == ''
10911
+ y = @y if y == ''
10912
+
10913
+ if js
10914
+ addfield('radiobutton', name, x, y, w, w, prop)
10915
+ return
10916
+ end
10917
+
10918
+ onvalue = 'On' if empty_string(onvalue)
10919
+ defval = checked ? onvalue : 'Off'
10920
+ # set data for parent group
10921
+ @radiobutton_groups[@page] ||= {}
10922
+ unless @radiobutton_groups[@page][name]
10923
+ @radiobutton_groups[@page][name] = []
10924
+ @annot_obj_id += 1
10925
+ @radio_groups << @annot_obj_id
10926
+ end
10927
+ # save object ID to be added on Kids entry on parent object
10928
+ @radiobutton_groups[@page][name] << {'kid' => @annot_obj_id + 1, 'def' => defval}
10929
+ # get default style
10930
+ prop = getFormDefaultProp.merge prop
10931
+ prop['NoToggleToOff'] = 'true'
10932
+ prop['Radio'] = 'true'
10933
+ prop['borderStyle'] = 'inset'
10934
+ # get annotation data
10935
+ popt = getAnnotOptFromJSProp(prop)
10936
+ # set additional default values
10937
+ font = 'zapfdingbats'
10938
+ AddFont(font)
10939
+ fontkey = @fontkeys.index font
10940
+ unless @annotation_fonts.include? fontkey
10941
+ @annotation_fonts[font] = fontkey
10942
+ end
10943
+ fontstyle = sprintf('/F%d %.2f Tf', fontkey + 1, @font_size_pt)
10944
+ popt['da'] = "#{fontstyle} #{@text_color}"
10945
+ popt['ap'] = {}
10946
+ popt['ap']['n'] = {}
10947
+ popt['ap']['n'][onvalue] = "q #{@text_color} BT #{fontstyle} 0 0 Td (n) Tj ET Q"
10948
+ popt['ap']['n']['Off'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (o) Tj ET Q"
10949
+ popt['mk'] ||= {}
10950
+ popt['mk']['ca'] = '(l)'
10951
+ # merge options
10952
+ opt = popt.merge opt
10953
+ # set remaining annotation data
10954
+ opt['Subtype'] = 'Widget'
10955
+ opt['ft'] = 'Btn'
10956
+ if checked
10957
+ opt['v'] = ["/#{onvalue}"]
10958
+ opt['as'] = onvalue
10959
+ else
10960
+ opt['as'] = 'Off'
10961
+ end
10962
+ Annotation(x, y, w, w, name, opt, 0)
10963
+ if @rtl
10964
+ @x -= w
10965
+ else
10966
+ @x += w
10967
+ end
10968
+ end
10969
+ alias_method :radio_button, :RadioButton
10970
+
10971
+ #
10972
+ # Creates a List-box field
10973
+ # [@param string :name] field name
10974
+ # [@param int :w] width
10975
+ # [@param int :h] height
10976
+ # [@param array :values] array containing the list of values.
10977
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10978
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
10979
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
10980
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
10981
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
10982
+ # [@access public]
10983
+ # [@author Nicola Asuni]
10984
+ # [@since 4.8.000 (2009-09-07)]
10985
+ #
10986
+ def ListBox(name, w, h, values, prop = {}, opt = {}, x = '', y = '', js = false)
10987
+ x = @x if x == ''
10988
+ y = @y if y == ''
10989
+
10990
+ if js
10991
+ addfield('listbox', name, x, y, w, h, prop)
10992
+ s = ''
10993
+ values.each {|v|
10994
+ if v.is_a?(Array)
10995
+ s << "['#{v[0]}','#{v[1]}'],"
10996
+ else
10997
+ s << "'#{v}',"
10998
+ end
10999
+ }
11000
+ @javascript << "f#{name}.setItems([#{s[0...-1]}]);\n"
11001
+ return
11002
+ end
11003
+ # get default style
11004
+ prop = getFormDefaultProp.merge prop
11005
+ # get annotation data
11006
+ popt = getAnnotOptFromJSProp(prop)
11007
+ # set additional default values
11008
+ font = @font_family
11009
+ fontkey = @fontkeys.index font
11010
+ unless @annotation_fonts.include? fontkey
11011
+ @annotation_fonts[font] = fontkey
11012
+ end
11013
+ s = ''
11014
+ values.each {|v|
11015
+ if v.is_a?(Array)
11016
+ s << "#{v[1]}\n"
11017
+ else
11018
+ s << "#{v}\n"
11019
+ end
11020
+ }
11021
+
11022
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11023
+ popt['da'] = fontstyle
11024
+ popt['ap'] = {}
11025
+ # set Appearances
11026
+ popt['ap']['n'] = "/Tx BMC q #{fontstyle} "
11027
+ gvars = getGraphicVars()
11028
+ @h = h
11029
+ @w = w
11030
+ @t_margin = 0
11031
+ @c_margin = 0.2
11032
+
11033
+ @tmp_buffer = ''
11034
+ multi_cell(w, h, s, 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
11035
+ popt['ap']['n'] << @tmp_buffer
11036
+ popt['ap']['n'] << 'Q EMC'
11037
+ @tmp_buffer = nil
11038
+
11039
+ # restore previous values
11040
+ setGraphicVars(gvars, true)
11041
+
11042
+ # merge options
11043
+ opt = popt.merge opt
11044
+ # set remaining annotation data
11045
+ opt['Subtype'] = 'Widget'
11046
+ opt['ft'] = 'Ch'
11047
+ opt['t'] = name
11048
+ opt['opt'] = values
11049
+ Annotation(x, y, w, h, name, opt, 0)
11050
+ if @rtl
11051
+ @x -= w
11052
+ else
11053
+ @x += w
11054
+ end
11055
+ end
11056
+ alias_method :list_box, :ListBox
11057
+
11058
+ #
11059
+ # Creates a Combo-box field
11060
+ # [@param string :name] field name
11061
+ # [@param int :w] width
11062
+ # [@param int :h] height
11063
+ # [@param array :values] array containing the list of values.
11064
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11065
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11066
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11067
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11068
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11069
+ # [@access public]
11070
+ # [@author Nicola Asuni]
11071
+ # [@since 4.8.000 (2009-09-07)]
11072
+ #
11073
+ def ComboBox(name, w, h, values, prop = {}, opt = {}, x = '', y = '', js = false)
11074
+ x = @x if x == ''
11075
+ y = @y if y == ''
11076
+
11077
+ if js
11078
+ addfield('combobox', name, x, y, w, h, prop)
11079
+ s = ''
11080
+ values.each {|v|
11081
+ if v.is_a?(Array)
11082
+ s << "['#{v[0]}','#{v[1]}'],"
11083
+ else
11084
+ s << "'#{v}',"
11085
+ end
11086
+ }
11087
+ @javascript << "f#{name}.setItems([#{s[0...-1]}]);\n"
11088
+ return
11089
+ end
11090
+ # get default style
11091
+ prop = getFormDefaultProp.merge prop
11092
+ prop['Combo'] = 'true'
11093
+ # get annotation data
11094
+ popt = getAnnotOptFromJSProp(prop)
11095
+ # set additional default options
11096
+ font = @font_family
11097
+ fontkey = @fontkeys.index font
11098
+ unless @annotation_fonts.include? fontkey
11099
+ @annotation_fonts[font] = fontkey
11100
+ end
11101
+
11102
+ s = ''
11103
+ values.each {|v|
11104
+ if v.is_a?(Array)
11105
+ s << "#{v[1]}\n"
11106
+ else
11107
+ s << "#{v}\n"
11108
+ end
11109
+ }
11110
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11111
+ popt['da'] = fontstyle
11112
+ popt['ap'] = {}
11113
+ # set Appearances
11114
+ popt['ap']['n'] = "/Tx BMC q #{fontstyle} "
11115
+ gvars = getGraphicVars()
11116
+ @h = h
11117
+ @w = w
11118
+ @t_margin = 0
11119
+ @c_margin = 0.2
11120
+
11121
+ @tmp_buffer = ''
11122
+ multi_cell(w, h, s, 0, '', 0, 0, 0.2, 0, true, 0, false, true, 0)
11123
+ popt['ap']['n'] << @tmp_buffer
11124
+ popt['ap']['n'] << 'Q EMC'
11125
+ @tmp_buffer = nil
11126
+
11127
+ # restore previous values
11128
+ setGraphicVars(gvars, true)
11129
+
11130
+ # merge options
11131
+ opt = popt.merge opt
11132
+ # set remaining annotation data
11133
+ opt['Subtype'] = 'Widget'
11134
+ opt['ft'] = 'Ch'
11135
+ opt['t'] = name
11136
+ opt['opt'] = values
11137
+ Annotation(x, y, w, h, name, opt, 0)
11138
+ if @rtl
11139
+ @x -= w
11140
+ else
11141
+ @x += w
11142
+ end
11143
+ end
11144
+ alias_method :combo_box, :ComboBox
11145
+
11146
+ #
11147
+ # Creates a CheckBox field
11148
+ # [@param string :name] field name
11149
+ # [@param int :w] width
11150
+ # [@param boolean :checked] define the initial state.
11151
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11152
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11153
+ # [@param string :onvalue] value to be returned if selected.
11154
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11155
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11156
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11157
+ # [@access public]
11158
+ # [@author Nicola Asuni]
11159
+ # [@since 4.8.000 (2009-09-07)]
11160
+ #
11161
+ def CheckBox(name, w, checked=false, prop = {}, opt = {}, onvalue = 'Yes', x = '', y = '', js = false)
11162
+ x = @x if x == ''
11163
+ y = @y if y == ''
11164
+
11165
+ if js
11166
+ addfield('checkbox', name, x, y, w, w, prop)
11167
+ return
11168
+ end
11169
+
11170
+ prop['value'] ||= ['Yes']
11171
+ # get default style
11172
+ prop = getFormDefaultProp.merge prop
11173
+ prop['borderStyle'] = 'inset'
11174
+ # get annotation data
11175
+ popt = getAnnotOptFromJSProp(prop)
11176
+ # set additional default options
11177
+ font = 'zapfdingbats'
11178
+ AddFont(font)
11179
+ fontkey = @fontkeys.index font
11180
+ unless @annotation_fonts.include? fontkey
11181
+ @annotation_fonts[font] = fontkey
11182
+ end
11183
+ fontstyle = sprintf('/F%d %.2f Tf', fontkey + 1, @font_size_pt)
11184
+ popt['da'] = "#{fontstyle} #{@text_color}"
11185
+ popt['ap'] = {}
11186
+ popt['ap']['n'] = {}
11187
+ popt['ap']['n']['Yes'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (n) Tj ET Q"
11188
+ popt['ap']['n']['Off'] = "q #{@text_color} BT #{fontstyle} 0 0 Td (o) Tj ET Q"
11189
+
11190
+ # merge options
11191
+ opt = popt.merge opt
11192
+ # set remaining annotation data
11193
+ opt['Subtype'] = 'Widget'
11194
+ opt['ft'] = 'Btn'
11195
+ opt['t'] = name
11196
+ if empty_string(onvalue)
11197
+ onvalue = 'Yes'
11198
+ end
11199
+ opt['opt'] = [onvalue]
11200
+ if checked
11201
+ opt['v'] = ['/Yes']
11202
+ opt['as'] = 'Yes'
11203
+ else
11204
+ opt['v'] = ['/Off']
11205
+ opt['as'] = 'Off'
11206
+ end
11207
+ Annotation(x, y, w, w, name, opt, 0)
11208
+ if @rtl
11209
+ @x -= w
11210
+ else
11211
+ @x += w
11212
+ end
11213
+ end
11214
+ alias_method :check_box, :CheckBox
11215
+
11216
+ #
11217
+ # Creates a button field
11218
+ # [@param string :name] field name
11219
+ # [@param int :w] width
11220
+ # [@param int :h] height
11221
+ # [@param string :caption] caption.
11222
+ # [@param mixed :action] action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
11223
+ # [@param array :prop] javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11224
+ # [@param array :opt] annotation parameters. Possible values are described on official PDF32000_2008 reference.
11225
+ # [@param float :x] Abscissa of the upper-left corner of the rectangle
11226
+ # [@param float :y] Ordinate of the upper-left corner of the rectangle
11227
+ # [@param boolean :js] if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11228
+ # [@access public]
11229
+ # [@author Nicola Asuni]
11230
+ # [@since 4.8.000 (2009-09-07)]
11231
+ #
11232
+ def Button(name, w, h, caption, action, prop = {}, opt = {}, x = '', y = '', js = false)
11233
+ x = @x if x == ''
11234
+ y = @y if y == ''
11235
+
11236
+ if js
11237
+ addfield('button', name, x, y, w, h, prop)
11238
+ @javascript << "f#{name}.buttonSetCaption('#{caption}');\n"
11239
+ @javascript << "f#{name}.setAction('MouseUp','#{action}');\n"
11240
+ @javascript << "f#{name}.highlight='push';\n"
11241
+ @javascript << "f#{name}.print=false;\n"
11242
+ return
11243
+ end
11244
+ # get default style
11245
+ prop = getFormDefaultProp.merge prop
11246
+ prop['Pushbutton'] = 'true'
11247
+ prop['highlight'] = 'push'
11248
+ prop['display'] = 'display.noPrint'
11249
+ # get annotation data
11250
+ popt = getAnnotOptFromJSProp(prop)
11251
+ # set additional default options
11252
+ popt['mk'] ||= {}
11253
+ popt['mk']['ca'] = textstring(caption)
11254
+ popt['mk']['rc'] = textstring(caption)
11255
+ popt['mk']['ac'] = textstring(caption)
11256
+ font = @font_family
11257
+ fontkey = @fontkeys.index font
11258
+ unless @annotation_fonts.include? fontkey
11259
+ @annotation_fonts[font] = fontkey
11260
+ end
11261
+ fontstyle = sprintf('/F%d %.2f Tf %s', fontkey + 1, @font_size_pt, @text_color)
11262
+ popt['da'] = fontstyle
11263
+ popt['ap'] = {}
11264
+ # set Appearances
11265
+ popt['ap']['n'] = "/Tx BMC q #{fontstyle} 0.800 g\n"
11266
+
11267
+ gvars = getGraphicVars()
11268
+ @h = h
11269
+ @w = w
11270
+ @c_margin *= 1.6
11271
+
11272
+ @tmp_buffer = ''
11273
+ SetLineStyle({'width' => 1.0, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => [231]})
11274
+ SetFillColor(204)
11275
+ multi_cell(w, h, caption, 1, 'C', 1, 0, 0, 0, true)
11276
+ popt['ap']['n'] << @tmp_buffer
11277
+ popt['ap']['n'] << 'Q EMC'
11278
+ @tmp_buffer = nil
11279
+
11280
+ # restore previous values
11281
+ setGraphicVars(gvars, true)
11282
+
11283
+ # merge options
11284
+ opt = popt.merge opt
11285
+ # set remaining annotation data
11286
+ opt['Subtype'] = 'Widget'
11287
+ opt['ft'] = 'Btn'
11288
+ opt['t'] = caption
11289
+ opt['v'] = name
11290
+ unless action.empty?
11291
+ if action.is_a?(Hash)
11292
+ # form action options as on section 12.7.5 of PDF32000_2008.
11293
+ opt['aa'] = '/D <<'
11294
+ bmode = ['SubmitForm', 'ResetForm', 'ImportData']
11295
+ action.each {|key, val|
11296
+ if (key == 'S') && bmode.include?(val)
11297
+ opt['aa'] << " /S /#{val}"
11298
+ elsif (key == 'F') && !val.empty?
11299
+ opt['aa'] << " /F #{datastring(val)}"
11300
+ elsif (key == 'Fields') && val.is_a?(Array) && !val.empty?
11301
+ opt['aa'] << ' /Fields ['
11302
+ val.each {|field|
11303
+ opt['aa'] << " #{textstring(field)}"
11304
+ }
11305
+ opt['aa'] << ']'
11306
+ elsif key == 'Flags'
11307
+ ff = 0
11308
+ if val.is_a?(Array)
11309
+ val.each {|flag|
11310
+ case flag
11311
+ when 'Include/Exclude'; ff |= 1 << 0
11312
+ when 'IncludeNoValueFields'; ff |= 1 << 1
11313
+ when 'ExportFormat'; ff |= 1 << 2
11314
+ when 'GetMethod'; ff |= 1 << 3
11315
+ when 'SubmitCoordinates'; ff |= 1 << 4
11316
+ when 'XFDF'; ff |= 1 << 5
11317
+ when 'IncludeAppendSaves'; ff |= 1 << 6
11318
+ when 'IncludeAnnotations'; ff |= 1 << 7
11319
+ when 'SubmitPDF'; ff |= 1 << 8
11320
+ when 'CanonicalFormat'; ff |= 1 << 9
11321
+ when 'ExclNonUserAnnots'; ff |= 1 << 10
11322
+ when 'ExclFKey'; ff |= 1 << 11
11323
+ when 'EmbedForm'; ff |= 1 << 13
11324
+ end
11325
+ }
11326
+ else
11327
+ ff = val.to_i
11328
+ end
11329
+ opt['aa'] << " /Flags #{ff}"
11330
+ end
11331
+ }
11332
+ opt['aa'] << ' >>'
11333
+ else
11334
+ # Javascript action or raw action command
11335
+ js_obj_id = addJavascriptObject(action)
11336
+ opt['aa'] = "/D #{js_obj_id} 0 R"
11337
+ end
11338
+ end
11339
+ Annotation(x, y, w, h, name, opt, 0)
11340
+ if @rtl
11341
+ @x -= w
11342
+ else
11343
+ @x += w
11344
+ end
11345
+ end
11346
+ alias_method :button, :Button
11347
+
10374
11348
  # --- END FORMS FIELDS ------------------------------------------------
10375
11349
 
10376
11350
  #
@@ -11378,7 +12352,39 @@ protected
11378
12352
  html = html_a + html_b + html[(pos + 6)..-1]
11379
12353
  offset = (html_a + html_b).length
11380
12354
  end
12355
+
12356
+ offset = 0
12357
+ while (offset < html.length) && (pos = html.index('</textarea>', offset))
12358
+ html_a = html[0, offset]
12359
+ html_b = html[offset, pos - offset + 11]
12360
+ while html_b =~ %r@<textarea([^\>]*)>(.*?)\n(.*?)</textarea>@mi
12361
+ # preserve newlines on <textarea> tag
12362
+ html_b.gsub!(%r@<textarea([^\>]*)>(.*?)\n(.*?)</textarea>@mi, "<textarea\\1>\\2<TBR>\\3</textarea>")
12363
+ html_b.gsub!(%r@<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>@mi, "<textarea\\1>\\2''\\3</textarea>")
12364
+ end
12365
+ html = html_a + html_b + html[(pos + 11)..-1]
12366
+ offset = html_a.length + html_b.length
12367
+ end
12368
+ html.gsub!(/([\s]*)<option/mi, "<option")
12369
+ html.gsub!(%r@</option>([\s]*)@mi, "</option>")
12370
+ offset = 0
12371
+ while (offset < html.length) && (pos = html.index('</option>', offset))
12372
+ html_a = html[0, offset]
12373
+ html_b = html[offset, pos - offset + 9]
12374
+ while html_b =~ %r@<option([^\>]*)>(.*?)</option>@mi
12375
+ html_b.gsub!(%r@<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>@mi, "\\2\t\\4\r")
12376
+ html_b.gsub!(%r@<option([^\>]*)>(.*?)</option>@mi, "\\2\r")
12377
+ end
12378
+ html = html_a + html_b + html[(pos + 9)..-1]
12379
+ offset = html_a.length + html_b.length
12380
+ end
12381
+ html.gsub!(/<select([^\>]*)>/mi, "<select\\1 opt=\"")
12382
+ html.gsub!(%r@([\s]+)</select>@mi, "\" />")
11381
12383
  html.gsub!(/[\n]/, " ")
12384
+
12385
+ # restore textarea newlines
12386
+ html.gsub!('<TBR>', "\n")
12387
+
11382
12388
  # remove extra spaces from code
11383
12389
  html.gsub!(/[\s]+<\/(table|tr|td|th|ul|ol|li|dl|dt|dd)>/, '</\\1>')
11384
12390
  html.gsub!(/[\s]+<(tr|td|th|ul|ol|li|dl|dt|dd|br)/, '<\\1')
@@ -11389,6 +12395,8 @@ protected
11389
12395
  html.gsub!(/[\s]*<img/, ' <img')
11390
12396
  html.gsub!(/<img([^\>]*)>/xi, '<img\\1><span><marker style="font-size:0"/></span>')
11391
12397
  html.gsub!(/<xre/, '<pre') # restore pre tag
12398
+ html.gsub!(/<textarea([^\>]*)>/xi, '<textarea\\1 value="')
12399
+ html.gsub!(/<\/textarea>/, '" />')
11392
12400
 
11393
12401
  # trim string
11394
12402
  html.gsub!(/^[\s]+/, '')
@@ -11520,7 +12528,7 @@ protected
11520
12528
  # *** opening html tag
11521
12529
  dom[key]['opening'] = true
11522
12530
  dom[key]['parent'] = level[-1]
11523
- if element[-1, 1] == '/' or (dom[key]['value'] =~ /(br|img|hr)/)
12531
+ if element[-1, 1] == '/' or (dom[key]['value'] =~ /(br|img|hr|input)/)
11524
12532
  # self-closing tag
11525
12533
  dom[key]['self'] = true
11526
12534
  else
@@ -11546,7 +12554,7 @@ protected
11546
12554
  dom[key]['text-indent'] = dom[parentkey]['text-indent']
11547
12555
  end
11548
12556
  # get attributes
11549
- attr_array = element.scan(/([^=\s]*)[\s]*=[\s]*"([^"]*)"/)
12557
+ attr_array = element.scan(/([^=\s]+)[\s]*(?:=[\s]*"([^"]*)")*/)[1..-1]
11550
12558
  dom[key]['attribute'] = {} # reset attribute array
11551
12559
  attr_array.each do |name, value|
11552
12560
  dom[key]['attribute'][name.downcase] = value
@@ -11982,8 +12990,9 @@ public
11982
12990
  def sanitize_html(html)
11983
12991
  # Escape '<' character for not tag case.
11984
12992
  html = html.gsub(%r{(<+)([^/a-zA-Z])}){CGI.escapeHTML($1) + $2}.gsub(%r{</([^a-zA-Z])}){'&lt;/' + $1}
11985
-
11986
- html = "%s" % sanitize(html, :tags=> %w(a b blockquote body br dd del div dl dt em font h1 h2 h3 h4 h5 h6 hr i img li ol p pre small span strong sub sup table td th thead tr tt u ins ul), :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href name dir class id nobr stroke strokecolor fill nested tablehead))
12993
+ "%s" % sanitize(html,
12994
+ :tags=> %w(a b blockquote body br dd del div dl dt em font form h1 h2 h3 h4 h5 h6 hr i img input label li ol option p pre select small span strong sub sup table td textarea th thead tr tt u ins ul),
12995
+ :attributes => %w(cellspacing cellpadding bgcolor color value width height src size colspan rowspan style align border face href name dir class id nobr stroke strokecolor fill nested tablehead cols rows type action enctype method maxlength onclick multiple checked disabled))
11987
12996
  end
11988
12997
  protected :sanitize_html
11989
12998
 
@@ -12083,6 +13092,7 @@ public
12083
13092
  prev_listordered = @listordered
12084
13093
  prev_listcount = @listcount
12085
13094
  prev_lispacer = @lispacer
13095
+ prev_li_position_x = @li_position_x
12086
13096
  @listnum = 0
12087
13097
  @listordered = []
12088
13098
  @listcount = []
@@ -12960,7 +13970,7 @@ public
12960
13970
  if !@premode
12961
13971
  prelen = dom[key]['value'].length
12962
13972
  if isRTLTextDir()
12963
- dom[key]['value'] = dom[key]['value'].rstrip + 0.chr
13973
+ dom[key]['value'] = dom[key]['value'].rstrip
12964
13974
  else
12965
13975
  dom[key]['value'] = dom[key]['value'].lstrip
12966
13976
  end
@@ -13155,6 +14165,7 @@ public
13155
14165
  @listordered = prev_listordered
13156
14166
  @listcount = prev_listcount
13157
14167
  @lispacer = prev_lispacer
14168
+ @li_position_x = prev_li_position_x
13158
14169
  dom = nil
13159
14170
  rescue => err
13160
14171
  Error('writeHTML Error.', err)
@@ -13471,6 +14482,7 @@ public
13471
14482
  @lispacer = '!'
13472
14483
  end
13473
14484
  end
14485
+ @li_position_x = @x
13474
14486
  when 'blockquote'
13475
14487
  if @rtl
13476
14488
  @r_margin += @listindent
@@ -13494,6 +14506,166 @@ public
13494
14506
  SetXY(GetX(), GetY() + ((0.3 * @font_size_pt) / @k))
13495
14507
  when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
13496
14508
  addHTMLVertSpace(hbz, hb, cell, firstorlast)
14509
+
14510
+ # Form fields (since 4.8.000 - 2009-09-07)
14511
+ when 'form'
14512
+ if tag['attribute']['action']
14513
+ @form_action = tag['attribute']['action']
14514
+ else
14515
+ @form_action = @@k_path_url + request&.base_url.to_s
14516
+ end
14517
+ if tag['attribute']['enctype']
14518
+ @form_enctype = tag['attribute']['enctype']
14519
+ else
14520
+ @form_enctype = 'application/x-www-form-urlencoded'
14521
+ end
14522
+ if tag['attribute']['method']
14523
+ @form_mode = tag['attribute']['method']
14524
+ else
14525
+ @form_mode = 'post'
14526
+ end
14527
+ when 'input'
14528
+ if tag['attribute']['type'] == 'checkbox' || (tag['attribute']['name'] && !empty_string(tag['attribute']['name']))
14529
+ name = tag['attribute']['name'] || rand.to_s
14530
+ prop = {}
14531
+ opt = {}
14532
+ if tag['attribute']['value'] && !empty_string(tag['attribute']['value'])
14533
+ value = tag['attribute']['value']
14534
+ end
14535
+ if tag['attribute']['maxlength'] && !empty_string(tag['attribute']['maxlength'])
14536
+ opt['maxlen'] = tag['attribute']['maxlength'].to_i
14537
+ end
14538
+ h = @font_size * @cell_height_ratio
14539
+ if tag['attribute']['size'] && !empty_string(tag['attribute']['size'])
14540
+ w = tag['attribute']['size'].to_i * GetStringWidth(32.chr) * 2
14541
+ else
14542
+ w = h
14543
+ end
14544
+ if tag['attribute'].key? 'disabled'
14545
+ prop['readonly'] = 'true'
14546
+ end
14547
+ if tag['attribute'].key? 'checked'
14548
+ checked = true
14549
+ else
14550
+ checked = false
14551
+ end
14552
+
14553
+ case tag['attribute']['type']
14554
+ when 'text'
14555
+ opt['v'] = value if value
14556
+ TextField(name, w, h, prop, opt, '', '', false)
14557
+ when 'password'
14558
+ opt['v'] = value if value
14559
+ prop['password'] = 'true'
14560
+ TextField(name, w, h, prop, opt, '', '', false)
14561
+ when 'checkbox'
14562
+ CheckBox(name, w, checked, prop, opt, value, '', '', false)
14563
+ when 'radio'
14564
+ RadioButton(name, w, prop, opt, value, checked, '', '', false)
14565
+ when 'submit'
14566
+ w = GetStringWidth(value) * 1.5
14567
+ h *= 1.6
14568
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
14569
+ action = {}
14570
+ action['S'] = 'SubmitForm'
14571
+ action['F'] = @form_action
14572
+ action['Flags'] = ['ExportFormat'] unless @form_enctype == 'FDF'
14573
+ action['Flags'] = ['GetMethod'] if @form_mode == 'get'
14574
+ Button(name, w, h, value, action, prop, opt, '', '', false)
14575
+ when 'reset'
14576
+ w = GetStringWidth(value) * 1.5
14577
+ h *= 1.6
14578
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
14579
+ Button(name, w, h, value, {'S'=>'ResetForm'}, prop, opt, '', '', false)
14580
+ when 'file'
14581
+ prop['fileSelect'] = 'true'
14582
+ TextField(name, w, h, prop, opt, '', '', false)
14583
+ value ||= '*'
14584
+ w = GetStringWidth(value) * 2
14585
+ h *= 1.2
14586
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
14587
+ jsaction = "var f=this.getField('#{name}'); f.browseForFileToSubmit();"
14588
+ Button('FB_' + name, w, h, value, jsaction, prop, opt, '', '', false)
14589
+ when 'hidden'
14590
+ opt['v'] = value if value
14591
+ opt['f'] = ['invisible', 'hidden']
14592
+ TextField(name, 0, 0, prop, opt, '', '', false)
14593
+ when 'image'
14594
+ # THIS TYPE MUST BE FIXED
14595
+ if tag['attribute']['src'] && !empty_string(tag['attribute']['src'])
14596
+ img = tag['attribute']['src']
14597
+ value = 'img'
14598
+ #opt['mk'] = {'i'=>img, 'tp'=>1, 'if'=>{'sw'=>'A', 's'=>'A', 'fb'=>false}}
14599
+ if tag['attribute']['onclick'] && !tag['attribute']['onclick'].empty?
14600
+ jsaction = tag['attribute']['onclick']
14601
+ else
14602
+ jsaction = ''
14603
+ end
14604
+ Button(name, w, h, value, jsaction, prop, opt, '', '', false)
14605
+ end
14606
+ when 'button'
14607
+ w = GetStringWidth(value) * 1.5
14608
+ h *= 1.6
14609
+ prop = {'lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>[196, 196, 196], 'strokeColor'=>[255, 255, 255]}
14610
+ if tag['attribute']['onclick'] && !tag['attribute']['onclick'].empty?
14611
+ jsaction = tag['attribute']['onclick']
14612
+ else
14613
+ jsaction = ''
14614
+ end
14615
+ Button(name, w, h, value, jsaction, prop, opt, '', '', false)
14616
+ end
14617
+ end
14618
+ when 'textarea'
14619
+ if tag['attribute']['name'] && !empty_string(tag['attribute']['name'])
14620
+ prop = {}
14621
+ opt = {}
14622
+ name = tag['attribute']['name']
14623
+ if tag['attribute']['value'] && !empty_string(tag['attribute']['value'])
14624
+ opt['v'] = tag['attribute']['value']
14625
+ end
14626
+ if tag['attribute']['cols'] && !empty_string(tag['attribute']['cols'])
14627
+ w = tag['attribute']['cols'].to_i * GetStringWidth(32.chr) * 2
14628
+ else
14629
+ w = 40
14630
+ end
14631
+ if tag['attribute']['rows'] && !empty_string(tag['attribute']['rows'])
14632
+ h = tag['attribute']['rows'].to_i * @font_size * @cell_height_ratio
14633
+ else
14634
+ h = 10
14635
+ end
14636
+ prop['multiline'] = 'true'
14637
+ TextField(name, w, h, prop, opt, '', '', false)
14638
+ end
14639
+ when 'select'
14640
+ if tag['attribute']['name'] && !empty_string(tag['attribute']['name']) && tag['attribute']['opt'] && !empty_string(tag['attribute']['opt'])
14641
+ h = @font_size * @cell_height_ratio
14642
+ if tag['attribute']['size'] && !empty_string(tag['attribute']['size'])
14643
+ h *= (tag['attribute']['size'].to_i + 1)
14644
+ end
14645
+ prop = {}
14646
+ opt = {}
14647
+ name = tag['attribute']['name']
14648
+ w = 0
14649
+ options = tag['attribute']['opt'].split("\r")
14650
+ values = []
14651
+ options.each {|val|
14652
+ if val.index("\t")
14653
+ opts = val.split("\t")
14654
+ values << opts
14655
+ w = [w, GetStringWidth(opts[1])].max
14656
+ else
14657
+ values << val
14658
+ w = [w, GetStringWidth(val)].max
14659
+ end
14660
+ }
14661
+ w *= 2
14662
+ if tag['attribute'].key? 'multiple'
14663
+ prop['multipleSelection'] = 'true'
14664
+ ListBox(name, w, h, values, prop, opt, '', '', false)
14665
+ else
14666
+ ComboBox(name, w, h, values, prop, opt, '', '', false)
14667
+ end
14668
+ end
13497
14669
  end
13498
14670
 
13499
14671
  if dom[key]['self'] and dom[key]['attribute']['pagebreakafter']
@@ -13569,317 +14741,325 @@ public
13569
14741
  end
13570
14742
  # Closing tag
13571
14743
  case (tag['value'])
13572
- when 'tr'
13573
- table_el = dom[(dom[key]['parent'])]['parent']
13574
- if parent['endy'].nil?
13575
- dom[(dom[key]['parent'])]['endy'] = @y
13576
- parent['endy'] = @y
14744
+ when 'tr'
14745
+ table_el = dom[(dom[key]['parent'])]['parent']
14746
+ if parent['endy'].nil?
14747
+ dom[(dom[key]['parent'])]['endy'] = @y
14748
+ parent['endy'] = @y
14749
+ end
14750
+ if parent['endpage'].nil?
14751
+ dom[(dom[key]['parent'])]['endpage'] = @page
14752
+ parent['endpage'] = @page
14753
+ end
14754
+ # update row-spanned cells
14755
+ if !dom[table_el]['rowspans'].nil?
14756
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
14757
+ dom[table_el]['rowspans'][k]['rowspan'] -= 1
14758
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
14759
+ if dom[table_el]['rowspans'][k]['endpage'] == parent['endpage']
14760
+ dom[(dom[key]['parent'])]['endy'] = [dom[table_el]['rowspans'][k]['endy'], parent['endy']].max
14761
+ elsif dom[table_el]['rowspans'][k]['endpage'] > parent['endpage']
14762
+ dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
14763
+ dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
14764
+ end
14765
+ end
14766
+ }
14767
+ # report new endy and endpage to the rowspanned cells
14768
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
14769
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
14770
+ dom[table_el]['rowspans'][k]['endpage'] = [dom[table_el]['rowspans'][k]['endpage'], dom[(dom[key]['parent'])]['endpage']].max
14771
+ dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
14772
+ dom[table_el]['rowspans'][k]['endy'] = [dom[table_el]['rowspans'][k]['endy'], dom[(dom[key]['parent'])]['endy']].max
14773
+ dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
14774
+ end
14775
+ }
14776
+ # update remaining rowspanned cells
14777
+ dom[table_el]['rowspans'].each_with_index { |trwsp, k|
14778
+ if dom[table_el]['rowspans'][k]['rowspan'] == 0
14779
+ dom[table_el]['rowspans'][k]['endpage'] = dom[(dom[key]['parent'])]['endpage']
14780
+ dom[table_el]['rowspans'][k]['endy'] = dom[(dom[key]['parent'])]['endy']
14781
+ end
14782
+ }
14783
+ end
14784
+ if (@num_columns > 1) and (dom[(dom[key]['parent'])]['endy'] >= (@page_break_trigger - @lasth)) and (@y < dom[(dom[key]['parent'])]['endy'])
14785
+ Ln(0, cell)
14786
+ else
14787
+ setPage(dom[(dom[key]['parent'])]['endpage']);
14788
+ @y = dom[(dom[key]['parent'])]['endy']
14789
+ if !dom[table_el]['attribute']['cellspacing'].nil?
14790
+ cellspacing = getHTMLUnitToUnits(dom[table_el]['attribute']['cellspacing'], 1, 'px')
14791
+ @y += cellspacing
13577
14792
  end
13578
- if parent['endpage'].nil?
13579
- dom[(dom[key]['parent'])]['endpage'] = @page
13580
- parent['endpage'] = @page
14793
+ Ln(0, cell)
14794
+ @x = parent['startx']
14795
+ # account for booklet mode
14796
+ if parent['startpage'] and @page > parent['startpage']
14797
+ if @rtl and (@pagedim[@page]['orm'] != @pagedim[parent['startpage']]['orm'])
14798
+ @x -= @pagedim[@page]['orm'] - @pagedim[parent['startpage']]['orm']
14799
+ elsif !@rtl and (@pagedim[@page]['olm'] != @pagedim[parent['startpage']]['olm'])
14800
+ @x += @pagedim[@page]['olm'] - @pagedim[parent['startpage']]['olm']
14801
+ end
13581
14802
  end
14803
+ end
14804
+ when 'table'
14805
+ if dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1"
14806
+ # closing tag used for the thead part
14807
+ in_table_head = true
14808
+ @in_thead = false
14809
+ end
14810
+
14811
+ table_el = parent
14812
+ # draw borders
14813
+ if (!table_el['attribute']['border'].nil? and (table_el['attribute']['border'].to_i > 0)) or (!table_el['style'].nil? and !table_el['style']['border'].nil? and (table_el['style']['border'].to_i > 0))
14814
+ border = 1
14815
+ else
14816
+ border = 0
14817
+ end
14818
+
14819
+ startpage = 0
14820
+ end_page = 0
14821
+ # fix bottom line alignment of last line before page break
14822
+ dom[(dom[key]['parent'])]['trids'].each_with_index { |trkey, j|
13582
14823
  # update row-spanned cells
13583
- if !dom[table_el]['rowspans'].nil?
13584
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13585
- dom[table_el]['rowspans'][k]['rowspan'] -= 1
13586
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13587
- if dom[table_el]['rowspans'][k]['endpage'] == parent['endpage']
13588
- dom[(dom[key]['parent'])]['endy'] = [dom[table_el]['rowspans'][k]['endy'], parent['endy']].max
13589
- elsif dom[table_el]['rowspans'][k]['endpage'] > parent['endpage']
13590
- dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
13591
- dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
13592
- end
14824
+ if !dom[(dom[key]['parent'])]['rowspans'].nil?
14825
+ dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
14826
+ if trwsp['trid'] == trkey
14827
+ dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] -= 1
13593
14828
  end
13594
- }
13595
- # report new endy and endpage to the rowspanned cells
13596
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13597
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13598
- dom[table_el]['rowspans'][k]['endpage'] = [dom[table_el]['rowspans'][k]['endpage'], dom[(dom[key]['parent'])]['endpage']].max
13599
- dom[(dom[key]['parent'])]['endpage'] = dom[table_el]['rowspans'][k]['endpage']
13600
- dom[table_el]['rowspans'][k]['endy'] = [dom[table_el]['rowspans'][k]['endy'], dom[(dom[key]['parent'])]['endy']].max
13601
- dom[(dom[key]['parent'])]['endy'] = dom[table_el]['rowspans'][k]['endy']
14829
+ if defined?(prevtrkey) and (trwsp['trid'] == prevtrkey) and (trwsp['mrowspan'] >= 0)
14830
+ dom[(dom[key]['parent'])]['rowspans'][k]['trid'] = trkey
13602
14831
  end
13603
14832
  }
13604
- # update remaining rowspanned cells
13605
- dom[table_el]['rowspans'].each_with_index { |trwsp, k|
13606
- if dom[table_el]['rowspans'][k]['rowspan'] == 0
13607
- dom[table_el]['rowspans'][k]['endpage'] = dom[(dom[key]['parent'])]['endpage']
13608
- dom[table_el]['rowspans'][k]['endy'] = dom[(dom[key]['parent'])]['endy']
13609
- end
13610
- }
13611
- end
13612
- if (@num_columns > 1) and (dom[(dom[key]['parent'])]['endy'] >= (@page_break_trigger - @lasth)) and (@y < dom[(dom[key]['parent'])]['endy'])
13613
- Ln(0, cell)
13614
- else
13615
- setPage(dom[(dom[key]['parent'])]['endpage']);
13616
- @y = dom[(dom[key]['parent'])]['endy']
13617
- if !dom[table_el]['attribute']['cellspacing'].nil?
13618
- cellspacing = getHTMLUnitToUnits(dom[table_el]['attribute']['cellspacing'], 1, 'px')
13619
- @y += cellspacing
13620
- end
13621
- Ln(0, cell)
13622
- @x = parent['startx']
13623
- # account for booklet mode
13624
- if parent['startpage'] and @page > parent['startpage']
13625
- if @rtl and (@pagedim[@page]['orm'] != @pagedim[parent['startpage']]['orm'])
13626
- @x -= @pagedim[@page]['orm'] - @pagedim[parent['startpage']]['orm']
13627
- elsif !@rtl and (@pagedim[@page]['olm'] != @pagedim[parent['startpage']]['olm'])
13628
- @x += @pagedim[@page]['olm'] - @pagedim[parent['startpage']]['olm']
13629
- end
13630
- end
13631
- end
13632
- when 'table'
13633
- if dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1"
13634
- # closing tag used for the thead part
13635
- in_table_head = true
13636
- @in_thead = false
13637
- end
13638
-
13639
- table_el = parent
13640
- # draw borders
13641
- if (!table_el['attribute']['border'].nil? and (table_el['attribute']['border'].to_i > 0)) or (!table_el['style'].nil? and !table_el['style']['border'].nil? and (table_el['style']['border'].to_i > 0))
13642
- border = 1
13643
- else
13644
- border = 0
13645
14833
  end
13646
-
13647
- startpage = 0
13648
- end_page = 0
13649
- # fix bottom line alignment of last line before page break
13650
- dom[(dom[key]['parent'])]['trids'].each_with_index { |trkey, j|
14834
+ if defined?(prevtrkey) and (dom[trkey]['startpage'] > dom[prevtrkey]['endpage'])
14835
+ pgendy = @pagedim[dom[prevtrkey]['endpage']]['hk'] - @pagedim[dom[prevtrkey]['endpage']]['bm']
14836
+ dom[prevtrkey]['endy'] = pgendy
13651
14837
  # update row-spanned cells
13652
14838
  if !dom[(dom[key]['parent'])]['rowspans'].nil?
13653
14839
  dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
13654
- if trwsp['trid'] == trkey
13655
- dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] -= 1
13656
- end
13657
- if defined?(prevtrkey) and (trwsp['trid'] == prevtrkey) and (trwsp['mrowspan'] >= 0)
13658
- dom[(dom[key]['parent'])]['rowspans'][k]['trid'] = trkey
14840
+ if (trwsp['trid'] == trkey) and (trwsp['mrowspan'] > 1) and (trwsp['endpage'] == dom[prevtrkey]['endpage'])
14841
+ dom[(dom[key]['parent'])]['rowspans'][k]['endy'] = pgendy
14842
+ dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] = -1
13659
14843
  end
13660
14844
  }
13661
14845
  end
13662
- if defined?(prevtrkey) and (dom[trkey]['startpage'] > dom[prevtrkey]['endpage'])
13663
- pgendy = @pagedim[dom[prevtrkey]['endpage']]['hk'] - @pagedim[dom[prevtrkey]['endpage']]['bm']
13664
- dom[prevtrkey]['endy'] = pgendy
13665
- # update row-spanned cells
13666
- if !dom[(dom[key]['parent'])]['rowspans'].nil?
13667
- dom[(dom[key]['parent'])]['rowspans'].each_with_index { |trwsp, k|
13668
- if (trwsp['trid'] == trkey) and (trwsp['mrowspan'] > 1) and (trwsp['endpage'] == dom[prevtrkey]['endpage'])
13669
- dom[(dom[key]['parent'])]['rowspans'][k]['endy'] = pgendy
13670
- dom[(dom[key]['parent'])]['rowspans'][k]['mrowspan'] = -1
13671
- end
13672
- }
13673
- end
14846
+ end
14847
+ prevtrkey = trkey
14848
+ table_el = dom[(dom[key]['parent'])].dup
14849
+ }
14850
+ # for each row
14851
+ table_el['trids'].each_with_index { |trkey, j|
14852
+ parent = dom[trkey]
14853
+ # for each cell on the row
14854
+ parent['cellpos'].each_with_index { |cellpos, k|
14855
+ if !cellpos['rowspanid'].nil? and (cellpos['rowspanid'] >= 0)
14856
+ cellpos['startx'] = table_el['rowspans'][(cellpos['rowspanid'])]['startx']
14857
+ cellpos['endx'] = table_el['rowspans'][(cellpos['rowspanid'])]['endx']
14858
+ endy = table_el['rowspans'][(cellpos['rowspanid'])]['endy']
14859
+ startpage = table_el['rowspans'][(cellpos['rowspanid'])]['startpage']
14860
+ end_page = table_el['rowspans'][(cellpos['rowspanid'])]['endpage']
14861
+ else
14862
+ endy = parent['endy']
14863
+ startpage = parent['startpage']
14864
+ end_page = parent['endpage']
13674
14865
  end
13675
- prevtrkey = trkey
13676
- table_el = dom[(dom[key]['parent'])].dup
13677
- }
13678
- # for each row
13679
- table_el['trids'].each_with_index { |trkey, j|
13680
- parent = dom[trkey]
13681
- # for each cell on the row
13682
- parent['cellpos'].each_with_index { |cellpos, k|
13683
- if !cellpos['rowspanid'].nil? and (cellpos['rowspanid'] >= 0)
13684
- cellpos['startx'] = table_el['rowspans'][(cellpos['rowspanid'])]['startx']
13685
- cellpos['endx'] = table_el['rowspans'][(cellpos['rowspanid'])]['endx']
13686
- endy = table_el['rowspans'][(cellpos['rowspanid'])]['endy']
13687
- startpage = table_el['rowspans'][(cellpos['rowspanid'])]['startpage']
13688
- end_page = table_el['rowspans'][(cellpos['rowspanid'])]['endpage']
13689
- else
13690
- endy = parent['endy']
13691
- startpage = parent['startpage']
13692
- end_page = parent['endpage']
13693
- end
13694
- cellpos['startx'] ||= 0
13695
- if end_page > startpage
13696
- # design borders around HTML cells.
13697
- startpage.upto(end_page) do |page|
13698
- setPage(page)
13699
- if page == startpage
13700
- @y = parent['starty'] # put cursor at the beginning of row on the first page
13701
- ch = getPageHeight() - parent['starty'] - getBreakMargin()
13702
- cborder = getBorderMode(border, position='start')
13703
- elsif page == end_page
13704
- @y = @t_margin # put cursor at the beginning of last page
13705
- ch = endy - @t_margin
13706
- cborder = getBorderMode(border, position='end')
13707
- else
13708
- @y = @t_margin # put cursor at the beginning of the current page
13709
- ch = getPageHeight() - @t_margin - getBreakMargin()
13710
- cborder = getBorderMode(border, position='middle')
13711
- end
13712
- if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
13713
- SetFillColorArray(cellpos['bgcolor'])
13714
- fill = 1
13715
- else
13716
- fill = 0
13717
- end
13718
- cw = (cellpos['endx'] - cellpos['startx']).abs
13719
- @x = cellpos['startx']
13720
- # account for margin changes
13721
- if page > startpage
13722
- if @rtl and (@pagedim[page]['orm'] != @pagedim[startpage]['orm'])
13723
- @x -= @pagedim[page]['orm'] - @pagedim[startpage]['orm']
13724
- elsif !@rtl and (@pagedim[page]['lm'] != @pagedim[startpage]['olm'])
13725
- @x += @pagedim[page]['olm'] - @pagedim[startpage]['olm']
13726
- end
13727
- end
13728
-
13729
- prevLastH = @lasth
13730
- # design a cell around the text
13731
- ccode = @fill_color + "\n" + getCellCode(cw, ch, '', cborder, 1, '', fill, '', 0, true)
13732
- @lasth = prevLastH
13733
-
13734
- if (cborder != 0) or (fill == 1)
13735
- pagebuff = getPageBuffer(@page)
13736
- pstart = pagebuff[0, @intmrk[@page]]
13737
- pend = pagebuff[@intmrk[@page]..-1]
13738
- setPageBuffer(@page, pstart + ccode + "\n" + pend)
13739
- @intmrk[@page] += (ccode + "\n").length
13740
- end
14866
+ cellpos['startx'] ||= 0
14867
+ if end_page > startpage
14868
+ # design borders around HTML cells.
14869
+ startpage.upto(end_page) do |page|
14870
+ setPage(page)
14871
+ if page == startpage
14872
+ @y = parent['starty'] # put cursor at the beginning of row on the first page
14873
+ ch = getPageHeight() - parent['starty'] - getBreakMargin()
14874
+ cborder = getBorderMode(border, position='start')
14875
+ elsif page == end_page
14876
+ @y = @t_margin # put cursor at the beginning of last page
14877
+ ch = endy - @t_margin
14878
+ cborder = getBorderMode(border, position='end')
14879
+ else
14880
+ @y = @t_margin # put cursor at the beginning of the current page
14881
+ ch = getPageHeight() - @t_margin - getBreakMargin()
14882
+ cborder = getBorderMode(border, position='middle')
13741
14883
  end
13742
- else
13743
- setPage(startpage)
13744
14884
  if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
13745
14885
  SetFillColorArray(cellpos['bgcolor'])
13746
14886
  fill = 1
13747
14887
  else
13748
14888
  fill = 0
13749
14889
  end
13750
- @x = cellpos['startx']
13751
- @y = parent['starty']
13752
14890
  cw = (cellpos['endx'] - cellpos['startx']).abs
13753
- ch = endy - parent['starty']
14891
+ @x = cellpos['startx']
14892
+ # account for margin changes
14893
+ if page > startpage
14894
+ if @rtl and (@pagedim[page]['orm'] != @pagedim[startpage]['orm'])
14895
+ @x -= @pagedim[page]['orm'] - @pagedim[startpage]['orm']
14896
+ elsif !@rtl and (@pagedim[page]['lm'] != @pagedim[startpage]['olm'])
14897
+ @x += @pagedim[page]['olm'] - @pagedim[startpage]['olm']
14898
+ end
14899
+ end
13754
14900
 
13755
14901
  prevLastH = @lasth
13756
14902
  # design a cell around the text
13757
- ccode = @fill_color + "\n" + getCellCode(cw, ch, '', border, 1, '', fill, '', 0, true)
14903
+ ccode = @fill_color + "\n" + getCellCode(cw, ch, '', cborder, 1, '', fill, '', 0, true)
13758
14904
  @lasth = prevLastH
13759
14905
 
13760
- if (border != 0) or (fill == 1)
13761
- if !@transfmrk[@page].nil?
13762
- pagemark = @transfmrk[@page]
13763
- @transfmrk[@page] += (ccode + "\n").length
13764
- elsif @in_footer
13765
- pagemark = @footerpos[@page]
13766
- @footerpos[@page] += (ccode + "\n").length
13767
- else
13768
- pagemark = @intmrk[@page]
13769
- @intmrk[@page] += (ccode + "\n").length
13770
- end
14906
+ if (cborder != 0) or (fill == 1)
13771
14907
  pagebuff = getPageBuffer(@page)
13772
- pstart = pagebuff[0, pagemark]
13773
- pend = pagebuff[pagemark..-1]
14908
+ pstart = pagebuff[0, @intmrk[@page]]
14909
+ pend = pagebuff[@intmrk[@page]..-1]
13774
14910
  setPageBuffer(@page, pstart + ccode + "\n" + pend)
14911
+ @intmrk[@page] += (ccode + "\n").length
13775
14912
  end
13776
14913
  end
13777
- }
13778
- if !table_el['attribute']['cellspacing'].nil?
13779
- cellspacing = getHTMLUnitToUnits(table_el['attribute']['cellspacing'], 1, 'px')
13780
- @y += cellspacing
13781
- end
13782
- Ln(0, cell)
13783
- @x = parent['startx']
13784
- if end_page > startpage
13785
- if @rtl and (@pagedim[end_page]['orm'] != @pagedim[startpage]['orm'])
13786
- @x += @pagedim[end_page]['orm'] - @pagedim[startpage]['orm']
13787
- elsif !@rtl and (@pagedim[end_page]['olm'] != @pagedim[startpage]['olm'])
13788
- @x += @pagedim[end_page]['olm'] - @pagedim[startpage]['olm']
14914
+ else
14915
+ setPage(startpage)
14916
+ if !cellpos['bgcolor'].nil? and (cellpos['bgcolor'] != false)
14917
+ SetFillColorArray(cellpos['bgcolor'])
14918
+ fill = 1
14919
+ else
14920
+ fill = 0
14921
+ end
14922
+ @x = cellpos['startx']
14923
+ @y = parent['starty']
14924
+ cw = (cellpos['endx'] - cellpos['startx']).abs
14925
+ ch = endy - parent['starty']
14926
+
14927
+ prevLastH = @lasth
14928
+ # design a cell around the text
14929
+ ccode = @fill_color + "\n" + getCellCode(cw, ch, '', border, 1, '', fill, '', 0, true)
14930
+ @lasth = prevLastH
14931
+
14932
+ if (border != 0) or (fill == 1)
14933
+ if !@transfmrk[@page].nil?
14934
+ pagemark = @transfmrk[@page]
14935
+ @transfmrk[@page] += (ccode + "\n").length
14936
+ elsif @in_footer
14937
+ pagemark = @footerpos[@page]
14938
+ @footerpos[@page] += (ccode + "\n").length
14939
+ else
14940
+ pagemark = @intmrk[@page]
14941
+ @intmrk[@page] += (ccode + "\n").length
14942
+ end
14943
+ pagebuff = getPageBuffer(@page)
14944
+ pstart = pagebuff[0, pagemark]
14945
+ pend = pagebuff[pagemark..-1]
14946
+ setPageBuffer(@page, pstart + ccode + "\n" + pend)
13789
14947
  end
13790
14948
  end
13791
14949
  }
13792
- if !in_table_head
13793
- # we are not inside a thead section
13794
- if dom[(parent['parent'])]['attribute']['cellpadding'] ### fix ###
13795
- @c_margin = @old_c_margin
14950
+ if !table_el['attribute']['cellspacing'].nil?
14951
+ cellspacing = getHTMLUnitToUnits(table_el['attribute']['cellspacing'], 1, 'px')
14952
+ @y += cellspacing
14953
+ end
14954
+ Ln(0, cell)
14955
+ @x = parent['startx']
14956
+ if end_page > startpage
14957
+ if @rtl and (@pagedim[end_page]['orm'] != @pagedim[startpage]['orm'])
14958
+ @x += @pagedim[end_page]['orm'] - @pagedim[startpage]['orm']
14959
+ elsif !@rtl and (@pagedim[end_page]['olm'] != @pagedim[startpage]['olm'])
14960
+ @x += @pagedim[end_page]['olm'] - @pagedim[startpage]['olm']
13796
14961
  end
13797
- @lasth = @font_size * @cell_height_ratio
13798
- if (@page == @numpages - 1) and @pageopen[@numpages]
13799
- # remove last blank page
13800
- deletePage(@numpages)
13801
- end
13802
- if !@thead_margins['top'].nil?
13803
- # restore top margin
13804
- @t_margin = @thead_margins['top']
13805
- @pagedim[@page]['tm'] = @t_margin
13806
- end
13807
- if table_el['attribute']['nested'].nil? or (table_el['attribute']['nested'] != 'true')
13808
- # reset main table header
13809
- @thead = ''
13810
- @thead_margins = {}
13811
- end
13812
- end
13813
- if tag['block']
13814
- unless dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1" ### fix ###
13815
- addHTMLVertSpace(hbz / 2, 0, cell, (dom[key+1].nil? or (dom[key+1]['value'] != 'table'))) ### fix ###
13816
- end
13817
- end
13818
- when 'a'
13819
- @href = {}
13820
- @html_anchor = nil
13821
- when 'sup'
13822
- SetXY(GetX(), GetY() + (0.7 * parent['fontsize'] / @k))
13823
- when 'sub'
13824
- SetXY(GetX(), GetY() - (0.3 * parent['fontsize'] / @k))
13825
- when 'div'
13826
- addHTMLVertSpace(hbz, 0, cell, firstorlast)
13827
- when 'blockquote'
13828
- if @rtl
13829
- @r_margin -= @listindent
13830
- else
13831
- @l_margin -= @listindent
13832
14962
  end
13833
- @listindentlevel -= 1
13834
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13835
- when 'p'
13836
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13837
- when 'pre'
13838
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13839
- @premode = false
13840
- when 'dl'
13841
- @listnum -= 1
13842
- if @listnum <= 0
13843
- @listnum = 0
13844
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13845
- else
13846
- addHTMLVertSpace(0, 0, cell, firstorlast)
14963
+ }
14964
+ if !in_table_head
14965
+ # we are not inside a thead section
14966
+ if dom[(parent['parent'])]['attribute']['cellpadding'] ### fix ###
14967
+ @c_margin = @old_c_margin
13847
14968
  end
13848
14969
  @lasth = @font_size * @cell_height_ratio
13849
- when 'dt'
13850
- @lispacer = ''
13851
- addHTMLVertSpace(0, 0, cell, firstorlast)
13852
- when 'dd'
13853
- @lispacer = ''
13854
- if @rtl
13855
- @r_margin -= @listindent
13856
- else
13857
- @l_margin -= @listindent
14970
+ if (@page == @numpages - 1) and @pageopen[@numpages]
14971
+ # remove last blank page
14972
+ deletePage(@numpages)
13858
14973
  end
13859
- @listindentlevel -= 1
13860
- addHTMLVertSpace(0, 0, cell, firstorlast)
13861
- when 'ul', 'ol'
13862
- @listnum -= 1
13863
- @lispacer = ''
13864
- if @rtl
13865
- @r_margin -= @listindent
13866
- else
13867
- @l_margin -= @listindent
14974
+ if !@thead_margins['top'].nil?
14975
+ # restore top margin
14976
+ @t_margin = @thead_margins['top']
14977
+ @pagedim[@page]['tm'] = @t_margin
13868
14978
  end
13869
- @listindentlevel -= 1
13870
- if @listnum <= 0
13871
- @listnum = 0
13872
- addHTMLVertSpace(hbz, hb, cell, firstorlast)
13873
- else
13874
- addHTMLVertSpace(0, 0, cell, firstorlast)
14979
+ if table_el['attribute']['nested'].nil? or (table_el['attribute']['nested'] != 'true')
14980
+ # reset main table header
14981
+ @thead = ''
14982
+ @thead_margins = {}
13875
14983
  end
13876
- @lasth = @font_size * @cell_height_ratio
13877
- when 'li'
13878
- @lispacer = ''
14984
+ end
14985
+ if tag['block']
14986
+ unless dom[(dom[key]['parent'])]['attribute']['tablehead'] and dom[(dom[key]['parent'])]['attribute']['tablehead'] == "1" ### fix ###
14987
+ addHTMLVertSpace(hbz / 2, 0, cell, (dom[key+1].nil? or (dom[key+1]['value'] != 'table'))) ### fix ###
14988
+ end
14989
+ end
14990
+ when 'a'
14991
+ @href = {}
14992
+ @html_anchor = nil
14993
+ when 'sup'
14994
+ SetXY(GetX(), GetY() + (0.7 * parent['fontsize'] / @k))
14995
+ when 'sub'
14996
+ SetXY(GetX(), GetY() - (0.3 * parent['fontsize'] / @k))
14997
+ when 'div'
14998
+ addHTMLVertSpace(hbz, 0, cell, firstorlast)
14999
+ when 'blockquote'
15000
+ if @rtl
15001
+ @r_margin -= @listindent
15002
+ else
15003
+ @l_margin -= @listindent
15004
+ end
15005
+ @listindentlevel -= 1
15006
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15007
+ when 'p'
15008
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15009
+ when 'pre'
15010
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15011
+ @premode = false
15012
+ when 'dl'
15013
+ @listnum -= 1
15014
+ if @listnum <= 0
15015
+ @listnum = 0
15016
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15017
+ else
13879
15018
  addHTMLVertSpace(0, 0, cell, firstorlast)
13880
- when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
15019
+ end
15020
+ @lasth = @font_size * @cell_height_ratio
15021
+ when 'dt'
15022
+ @lispacer = ''
15023
+ @li_position_x = nil
15024
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15025
+ when 'dd'
15026
+ @lispacer = ''
15027
+ @li_position_x = nil
15028
+ if @rtl
15029
+ @r_margin -= @listindent
15030
+ else
15031
+ @l_margin -= @listindent
15032
+ end
15033
+ @listindentlevel -= 1
15034
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15035
+ when 'ul', 'ol'
15036
+ @listnum -= 1
15037
+ @lispacer = ''
15038
+ @li_position_x = nil
15039
+ if @rtl
15040
+ @r_margin -= @listindent
15041
+ else
15042
+ @l_margin -= @listindent
15043
+ end
15044
+ @listindentlevel -= 1
15045
+ if @listnum <= 0
15046
+ @listnum = 0
13881
15047
  addHTMLVertSpace(hbz, hb, cell, firstorlast)
15048
+ else
15049
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15050
+ end
15051
+ @lasth = @font_size * @cell_height_ratio
15052
+ when 'li'
15053
+ @lispacer = ''
15054
+ @li_position_x = nil
15055
+ addHTMLVertSpace(0, 0, cell, firstorlast)
15056
+ when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
15057
+ addHTMLVertSpace(hbz, hb, cell, firstorlast)
15058
+ when 'form' # Form fields (since 4.8.000 - 2009-09-07)
15059
+ @form_action = ''
15060
+ @form_enctype = 'application/x-www-form-urlencoded'
13882
15061
  end
15062
+
13883
15063
  if dom[(dom[key]['parent'])]['attribute']['pagebreakafter']
13884
15064
  pba = dom[(dom[key]['parent'])]['attribute']['pagebreakafter']
13885
15065
  # check for pagebreak
@@ -14119,6 +15299,7 @@ protected
14119
15299
  width = 0
14120
15300
  textitem = ''
14121
15301
  tmpx = @x
15302
+ @x = @li_position_x
14122
15303
  lspace = GetStringWidth(' ')
14123
15304
  if listtype == '!'
14124
15305
  # set default list type for unordered list
@@ -14190,6 +15371,7 @@ protected
14190
15371
  end
14191
15372
  @x = tmpx
14192
15373
  @lispacer = ''
15374
+ @li_position_x = nil
14193
15375
  end
14194
15376
 
14195
15377
  #
@@ -14206,6 +15388,7 @@ protected
14206
15388
  'rMargin' => @r_margin,
14207
15389
  'lMargin' => @l_margin,
14208
15390
  'cMargin' => @c_margin,
15391
+ 'tMargin' => @t_margin,
14209
15392
  'LineWidth' => @line_width,
14210
15393
  'linestyleWidth' => @linestyle_width,
14211
15394
  'linestyleCap' => @linestyle_cap,
@@ -14226,24 +15409,31 @@ protected
14226
15409
  'listordered' => @listordered,
14227
15410
  'listcount' => @listcount,
14228
15411
  'lispacer' => @lispacer,
14229
- 'lasth' => @lasth
15412
+ 'li_position_x' => @li_position_x,
15413
+ 'lasth' => @lasth,
15414
+ 'h' => @h,
15415
+ 'w' => @w,
15416
+ 'x' => @x,
15417
+ 'y' => @y,
14230
15418
  }
14231
15419
  return grapvars
14232
15420
  end
14233
15421
 
14234
15422
  #
14235
15423
  # Set graphic variables.
14236
- # [@param :gvars] array graphic variables
15424
+ # [@param array :gvars] graphic variables
15425
+ # [@param bool :option] set additional parameters
14237
15426
  # [@access protected]
14238
15427
  # [@since 4.2.010 (2008-11-14)]
14239
15428
  #
14240
- def setGraphicVars(gvars)
15429
+ def setGraphicVars(gvars, option = false)
14241
15430
  @font_family = gvars['FontFamily']
14242
15431
  @font_style = gvars['FontStyle']
14243
15432
  @font_size_pt = gvars['FontSizePt']
14244
15433
  @r_margin = gvars['rMargin']
14245
15434
  @l_margin = gvars['lMargin']
14246
15435
  @c_margin = gvars['cMargin']
15436
+ @t_margin = gvars['tMargin']
14247
15437
  @line_width = gvars['LineWidth']
14248
15438
  @linestyle_width = gvars['linestyleWidth']
14249
15439
  @linestyle_cap = gvars['linestyleCap']
@@ -14264,7 +15454,14 @@ protected
14264
15454
  @listordered = gvars['listordered']
14265
15455
  @listcount = gvars['listcount']
14266
15456
  @lispacer = gvars['lispacer']
14267
- #@lasth = gvars['lasth']
15457
+ @li_position_x = gvars['li_position_x']
15458
+ if option
15459
+ @lasth = gvars['lasth']
15460
+ @h = gvars['h']
15461
+ @w = gvars['w']
15462
+ @x = gvars['x']
15463
+ @y = gvars['y']
15464
+ end
14268
15465
  out('' + @linestyle_width + ' ' + @linestyle_cap + ' ' + @linestyle_join + ' ' + @linestyle_dash + ' ' + @draw_color + ' ' + @fill_color + '')
14269
15466
  unless empty_string(@font_family)
14270
15467
  SetFont(@font_family, @font_style, @font_size_pt)
@@ -14671,18 +15868,17 @@ public
14671
15868
  #global jfrompage, jtopage
14672
15869
  #jfrompage = frompage
14673
15870
  #jtopage = topage
14674
- #@javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
14675
- #create_function('$matches', 'global $jfrompage, $jtopage;
14676
- # pagenum = matches[3].to_i + 1
14677
- # if (pagenum >= jtopage) and (pagenum < jfrompage)
14678
- # newpage = pagenum + 1
14679
- # elsif pagenum == jfrompage
14680
- # newpage = jtopage
14681
- # else
14682
- # newpage = pagenum
14683
- # end
14684
- # newpage -= 1
14685
- # return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
15871
+ #tmpjavascript =~ /this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/
15872
+ #pagenum = $3.to_i + 1
15873
+ #if (pagenum >= jtopage) && (pagenum < jfrompage)
15874
+ # newpage = pagenum + 1
15875
+ #elsif pagenum == jfrompage
15876
+ # newpage = jtopage
15877
+ #else
15878
+ # newpage = pagenum
15879
+ #end
15880
+ #newpage -= 1
15881
+ #@javascript = "this.addField(\'" + $1 + "\',\'" + $2 + "\'," + newpage + ""
14686
15882
 
14687
15883
  # return to last page
14688
15884
  lastPage(true)
@@ -14865,6 +16061,8 @@ public
14865
16061
  else
14866
16062
  gap = ''
14867
16063
  end
16064
+ # sort outlines by page and original position
16065
+ @outlines = @outlines.sort_by {|x| [x[:p], x[:y]] }
14868
16066
  @outlines.each_with_index do |outline, key|
14869
16067
  if @rtl
14870
16068
  aligntext = 'R'
@@ -15004,6 +16202,8 @@ public
15004
16202
  @html_link_color_array = []
15005
16203
  @html_link_font_style = ''
15006
16204
  page_first = getPage()
16205
+ # sort outlines by page and original position
16206
+ @outlines = @outlines.sort_by {|x| [x[:p], x[:y]] }
15007
16207
  @outlines.each_with_index do |outline, key|
15008
16208
  if empty_string(page)
15009
16209
  pagenum = outline[:p].to_s