I have stumbled on a very annoying problem when setting column widths on a table in Word (using Microsoft Office 15.0 Object Library, VS 2013). If I run the code below without having any breakpoints, the result becomes incorrect (first column width should be 30%), but if I put a breakpoint (e.g.) on line 47, the generated Word file becomes as I want it to be.
The conclusion I make is that when the debugger stops execution, the given column size values are flushed into the data model and the output will be as I want it to be. Without the breakpoint, the merging of cells changes the widths (e.g. the first column becomes 12,5%).
I have searched for some sort of method/function to make the data model adjust to my programmatically given column sizes before the cell merging, with no luck. Anyone out there that can explain why halting on the breakpoint will change the output?
Regards,
Ola
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Word;
namespace ShowWordProblem
{
class Program
{
private const string WordFileName = #"C:\temp\test.doc";
static void Main(string[] args)
{
var wordApplication = new Application();
wordApplication.Visible = false;
var wordDocument = wordApplication.Documents.Add();
FillDocument(wordDocument);
wordDocument.SaveAs2(WordFileName);
wordDocument.Close();
wordApplication.Quit(Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(wordApplication);
wordApplication = null;
wordApplication = new Microsoft.Office.Interop.Word.Application();
wordApplication.Visible = true;
wordApplication.Documents.Open(WordFileName);
}
private static void FillDocument(Document wordDocument)
{
Range range = wordDocument.Content.Paragraphs.Add().Range;
var table = range.Tables.Add(range, 5, 8);
table.PreferredWidthType = WdPreferredWidthType.wdPreferredWidthPercent;
table.PreferredWidth = (float)100.0;
table.Columns.PreferredWidthType = WdPreferredWidthType.wdPreferredWidthPercent;
table.Columns.PreferredWidthType = WdPreferredWidthType.wdPreferredWidthPercent;
table.Columns[1].PreferredWidth = 30;
for (int i = 2; i <= 8; i++) table.Columns[i].PreferredWidth = 10;
var widths = table.Columns.OfType<Column>().Select(c => c.Width).ToArray();
MergeGroupHeaderCells(table.Rows[1], 5, 9);
MergeGroupHeaderCells(table.Rows[1], 2, 5);
}
private static void MergeGroupHeaderCells(Row row, int startIndex, int lastIndex)
{
var r = row.Cells[startIndex].Range;
Object cell = WdUnits.wdCell;
Object count = lastIndex - startIndex - 1;
r.MoveEnd(ref cell, ref count);
r.Cells.Merge();
}
}
}
Finally I found a way to force the code to apply the given percentage values to the columns before the merging of the cells, and thereby having the correct widths on all columns.
By adding the following line of code after the last PreferredWidth-assignment:
var info = table.Range.Information[WdInformation.wdWithInTable];
it seems that the given PreferredWidth values are propagated to the columns, and the final rendered word document looks exactly as I want it.
/Ola
Related
I have following code to create a single pdf with 2 columns and 4 rows. Every cell contains an image.
int labelCount = x;
int columns = 2;
int labelsPerPage = 8;
int rows = labelCount/columns;
int resto = labelCount%columns;
if(resto>0) rows++;
String dest = "path_to_pdf_file";
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
doc.setMargins(0, 0, 0, 0);
Table table = new Table(UnitValue.createPercentArray(columns)).useAllAvailableWidth();
Integer iCount = 0;
for (int i = 1; i <= rows; i++) {
for (int y = 1; y <= columns; y++) {
if(iCount<labelCount) {
String fileName = "name_of_image";
Cell cell = new Cell();
cell.add(new com.itextpdf.layout.element.Image(ImageDataFactory.create("full_path_to_image")).setAutoScale(true));
cell.setBorder(Border.NO_BORDER);
table.addCell(cell);
iCount++;
if(iCount==labelsPerPage-1) {
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
}
}
doc.add(table);
doc.close();
If number of labels is bigger than defined limit (8 per page), I want a new page to be created with the following labels.
In my code I used
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
but it generate (I don't know why) a blank page at the beginning. In the second page there are labels.
Which is the right way to add a new page dynamically and put remaining content into it?
Thanks
Essentially in your code you add AreaBreak to the document before you add the table, that's why the blank page is inserted before the table. iText 7 does not allow you to insert page breaks within tables at this point.
To achieve the desired result you need to flush the existing table to the document before moving on to the next page, and create a new table for the next page. Essentially you will be adding a bunch of tables separated with AreaBreak.
The pseudocode which does not require a lot of modifications to your code could look as follows:
Table table = new Table();
for (...) {
// populate table
if ("it's time to insert a page break") {
doc.add(table);
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
table = new Table(); // create new table
}
}
doc.add(table);
I'm doing a project where I'm outputting the frequencies from real time mic input through fft to a txt doc and then retrieving (or trying to retrieve) them to a list of 4 frequencies. My list array is turning out empty, ie the console prints [] and no numbers in them. Pl tell me what is wrong with the logic/code. This is within the void draw()
for (int i = 0; i<fft.specSize(); i++) {
float freq = fft.getFreq(i);
int freqint = (int) freq;
//println(freqint);
output.println(freqint);}
Scanner input = new Scanner("...\\list.txt");
while (input.hasNextInt()) {
list.get(input.nextInt(4));
}
println(list);
input.close();
Split your taks into subtasks:
get FFT data (and 4 frequencies)
Save/Load float values to disk
Put the 1 and 2 together
It looks like you already have the first part done.
It's unclear what the 4 frequencies are, but you can figure that out on your own.
The part that is confusing is that you call the get FFT data once then try to save it straight to disk. I'm not 100% that's what you mean to do.
Double check if you need to save few samples/seconds worth of FFT data first.
I would imagine a more generic scenario like this:
load previously saved FFT data (if any)
process FFT data (for as long as needed - might need a start/stop boolean) and append
save current FFT data
Moving on to saving and loading data.
You've got a few of options build into Processing for storing data:
Strings via saveStrings()/loadStrings()
CSV Table via saveTable()/loadTable()/Table
JSON via JSONObject/JSONArray and available load/save functions
XML
I recommend reading Daniel Shiffman's Data Tutorial
Dealing with saving/loading floating point values to disk alone,
here's a basic proof of concept snippet using Strings:
int fftSpecSize = 10;//this will be fft.specSize() in your case
String values = "";
for(int i = 0 ; i < fftSpecSize; i++){
//random(1) is a placeholder for fft.getFreq(i);
values += random(1);
//if it's not the last value, add a separator character (in this case space)
if(i < fftSpecSize-1){
values += " ";
}
}
println("values to save:");
println(values);
println();
//save data
saveStrings("fftSingleRead.txt",values.split(" "));
//load data
String[] readValues = loadStrings("fftSingleRead.txt");
//print raw loaded data
println("readValues from .txt file:");
println(readValues);
println();
//parse values:
for(int i = 0 ; i < readValues.length; i++){
float value = float(readValues[i]);
println("readValues[",i,"] = ",value);
}
additionally here's a Table example too:
Table fftValues;
int specSize = 10;
//create a new table
fftValues = new Table();
//add columns - one per FFT bin
for(int i = 0 ; i < specSize; i++){
fftValues.addColumn();
}
//add some data, a row per reading
//create a new row
TableRow newRow = fftValues.addRow();
//add each fft value to the row
for(int fftCount = 0 ; fftCount < specSize; fftCount++){
//placeholder for newRow.setFloat(fftCount,fft.getFreq(fftCount));
newRow.setFloat(fftCount,random(1));
}
//add the row to the table
fftValues.addRow(newRow);
//save to disk
saveTable(fftValues,"fftValues.csv","csv");
//load from disk
Table readValues = loadTable("fftValues.csv","csv");
//access the data that has been saved in the table
for(int rowCount = 0; rowCount < readValues.getRowCount(); rowCount++){
TableRow currentRow = readValues.getRow(rowCount);
print("row[",rowCount,"] = ");
for(int columnCount = 0; columnCount < currentRow.getColumnCount(); columnCount++){
float value = currentRow.getFloat(columnCount);
print(value);
if(columnCount < currentRow.getColumnCount() - 1){
print(",");
}
}
println();
}
Try the code, see the output, read the comments, read the reference, tweak/retry/understand and adapt to your problem.
I'm trying to create a drop down menu with contents based on a another cell in the same row. For example if A1 = 'yes' then the drop down in B2 gives you the options of 'yes' or 'no'. I can do this I have the list data set up and to code works. The problem is I need to do this 155 times in 4 different sheets. Is there a faster way to do this than right clicking and editing the data validation rules for each cell. Here's a link to the test sheet I'm working on :
https://docs.google.com/spreadsheets/d/1rd_Ig_wpof9R_L0IiA1aZ9syO7BWxb6jvBhPqG8Jmm4/edit?usp=sharing
You can set data validation rules with a script, as documented here. Here's a reference for starting with Apps scripts.
I wrote a function that does approximately what you described. It works with the range B3:B157 of the sheet '9th grade' in the current spreadsheet. For each of them, it sets the validation rule to be: a value in the same row, columns B and C of sheet 'List Data'. The line with
....... = listData.getRange(i+3, 2, 1, 2);
will need to be modified if the source range of validation is to be different. Here, the parameters are: starting row, starting column, number of rows, number of columns. So, 2 columns starting with the second, in row numbered i+3.
function setRules() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var grade = ss.getSheetByName('9th Grade');
var listData = ss.getSheetByName('List Data');
var range = grade.getRange('B3:B157');
var rules = range.getDataValidations();
for (var i = 0; i < rules.length; i++) {
var sourceRange = listData.getRange(i+3, 2, 1, 2);
rules[i][0] = SpreadsheetApp.newDataValidation().requireValueInRange(sourceRange).build();
}
range.setDataValidations(rules);
}
I land in this issue for a diferent reason: "Just mass DataValidation copy (or update) in one column". Thanks, to user3717023 that bring me a light.
I hope that helps someone this simplification.
function setRules() {
//select spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var leads = ss.getSheetByName('Leads');
//Select correct Datavalidation
var rangeNewDataValidation = leads.getRange('M2:M2');
var rule = rangeNewDataValidation.getDataValidations();
//Copy (or Update) Datavalidation in a specific (13 or 'M') column
var newRule = rule[0][0].copy();
Logger.log(leads.getMaxRows())
for( var i=3; i <= leads.getMaxRows(); i++){
var range = leads.getRange(i, 13);
range.setDataValidations([[newRule.build()]]);
}
}
I'm studying Java so I'm pretty new.
I'm creating a simple 'maze' type game using GUI layouts, images, labels ect..
To create my maze layouts I used an array of strings;
mazeLayout[0] = "WWWWWWWWWW";
mazeLayout[1] = "WSSSWWSWWW";
mazeLayout[2] = "WSWSWWSSSW";
mazeLayout[3] = "WSWSWWWWSW";
mazeLayout[4] = "WSWSWWWWSW";
mazeLayout[5] = "WSWSWSSSSW";
mazeLayout[6] = "WSWSWSWWWW";
mazeLayout[7] = "WSWSWSWWWW";
mazeLayout[8] = "WSWSSSWWWW";
mazeLayout[9] = "WWWWWWWWWW";
and then converted this into a 2d array and placed a label with in image icon in it depending on the string being 'W' for wall or 'S' for space. Also the labels are an array, my thoughts behind this was for restricting movement of the player so they can't walk though walls.
int mw = 0;
int mf = 0;
for(int y = 0; y < 10; y++){
for(int x = 0; x < 10; x++){
mazeLayout2d[y][x] = mazeLayout[y].substring(x, x+1);
if (mazeLayout2d[y][x].equals("W")){
lblmazewall[mw] = new JLabel();
mazewall = new ImageIcon("mazewall.png");
lblmazewall[mw].setIcon(mazewall);
pCenter.add(lblmazewall[mw]);
mw++;
pCenter.revalidate();
}
if (mazeLayout2d[y][x].equals("S")){
lblmazefloor[mf] = new JLabel();
mazefloor = new ImageIcon("mazefloor.png");
lblmazefloor[mf].setIcon(mazefloor);
pCenter.add(lblmazefloor[mf]);
mf++;
pCenter.revalidate();
}
}
}
My problem is when i run this line
System.out.println(lblmazewall[x].getLocation()); //x being any number
I always get java.awt.Point[x=0,y=0]
I would like to know how to get the location of each wall label so i can check it against my player movement.
Is this even a valid way to do something like this?
Could someone teach me a more efficient way?
Sorry for my crude snippets and or bad programming
Thankyou Niall.
public Point getLocation()
Due to the asynchronous nature of native event handling, this method can return outdated values (for instance, after several calls of setLocation() in rapid succession). For this reason, the recommended method of obtaining a component's position is within java.awt.event.ComponentListener.componentMoved(), which is called after the operating system has finished moving the component.
The layout might not have used setLocation() internally. So that getLocation() does not return the value as expected.
If I want to change the color of circles in scatter chart based on a field not being used in the chart, then how do i use that column in script. I mean how can i get the that data...for example
If (row[v_count])>2
fill red color...
The exact code is below
function beforeDrawDataPoint(dph, fill, icsc)
{
//Fill implements Fill interface
//ImageImpl
//ColorDefinitionImpl
//GradientImpl
//MultipleFillImpl
//EmbeddedImageImpl
//PatternImageImpl
importPackage( Packages.org.eclipse.birt.chart.model.attribute.impl );
val = dph.getOrthogonalValue();
if( fill.getClass().isAssignableFrom(ColorDefinitionImpl)){
if (row[v_count]>2){
fill.set(255, 0, 0);
}
}
}
but i dont know do i get that v_count column in the script. is there some function to get that column ?
I mean if we are making some calculations based on a column from databinding columns..that is not being used in x or y axis, then how do we access that column in the script..is there some kind of function for that.. I tried row["v_count"], but it is not working.
Arif
You could use "persistent global variables". In any place of your report you can write the following to store and load a global variable. Note that you cannot store Integers but only Strings (but after loading you can cast your Strings back to other types). You could store the Value of your column in the Script of an invisible data field located above your chart, so inside your chart you can read the value.
//store a value
reportContext.setPersistentGlobalVariable("varName", "value");
//load the value
var load = reportContext.getPersistentGlobalVariable("varName");
I spend my day look but I did not find the solution, this is my colleague who give me :)
So I share.
in my example I had to create a vertical marker for every new year
function beforeGeneration(chart, icsc)
{
importPackage(Packages.org.eclipse.birt.chart.model.component.impl);
importPackage(Packages.org.eclipse.birt.chart.model.data.impl);
importPackage(Packages.org.eclipse.birt.chart.model.attribute);
importPackage(Packages.org.eclipse.birt.chart.model.attribute.impl);
var chart = icsc.getChartInstance();
var yAxis = chart.getBaseAxes()[0];
//get date series for my case
series = yAxis.getRuntimeSeries();
// but if you have multiple series ... (for exemple xaxis)
for (i = 0; i < series.length; i++){
var values = series[i].getDataSet().getValues();
for (j = 0; j < values.length; j++){
if(j > 1){
var date1 = values[j-1];
var date2 = values[j];
if(date1.getYear() < date2.getYear()){
min_ml = MarkerLineImpl.create(yAxis, NumberDataElementImpl.create(j));
min_ml.getLabel().getCaption().setValue("Nouveau boitier");
min_ml.getLineAttributes().getColor().set(255,0,0);
}
}
}
}
}