Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

Bruno Damas

Como guardar ficheiros PDF num programa

Mensagens Recomendadas

Bruno Damas

Viva Amigos,

Cá estou eu mais uma vez com umas duvidas.

Eu num projecto que estou a fazer gostaria de guardar ficheiros em PDF (são digitalizados para pdf e seria só guardado no projecto agregado a um id para mais tarde poder visualizar e imprimir) só não sei como o fazer se na BD ou numa pasta (e se for numa pasta, eu tenho que criar-la antes do executável),

Será possivel colocar um datetimepicker numa datagridview (combox existe e outros mas o datetpicker nao)

Uma outra duvida que tenho é que o programa vai ser para vários utilizadores (uns com W7 outros com vista, isto será um problema?) em simultâneo e onde vou ter que instalar o executável no servidor e nas restantes maquinas? e a Base dados?

Obrigado

Desculpem pela minha ignorância.  :-[ 

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Tuntankamon

Tanta confusão...

1º - PDFs, se forem pequenos e a quantidade não for muita, podem ir directamente para a bd, caso contrário, deverás criar uma pasta num local onde todas as máquinas tenham acesso, e copiar/mover para lá os PDFs (melhor opção quanto a mim). Para a questão da pasta só tens de verificar de cada vez que o programa arranca se a pasta existe, caso não exista crias (atenção às permissões).

If Not IO.Directory.Exists("PATH") Then
            IO.Directory.CreateDirectory("PATH")
End If

2º - Existir não existe, mas podes criar um, overload da datagridviewcolumn.

3º - Em relação aos diferentes windows, normalmente de windows 7/vista não existe grande diferença, mas se for destes dois para o xp, aí já existe muita, devido ao controlo de conta do utilizador, por isso aconselho a testares bem. Em relação à utilização em simultâneo podes ter problemas se os utilizadores tentarem alterar os mesmos dados na bd ao mesmo e tu não tiveres um controlo de concorrência como deve ser. A base de dados, se é para ser um meio partilhado deverá ser por exemplo num sql server ou algo do género, pois base de dados em access partilhadas, não é grande politica.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Bruno Damas

Tens razão r00tfixxxer "Tanta Confusão" por isso estou aqui a tirar duvidas e era também para aproveitar o post.

Em relação:

1º-Eu também perferia criar uma pasta no servidor, este código é para criar a pasta quando ela não existe?

2º- Podias ser mais especifico?

3º tendo em conta que a BD em que tenho estado a trabalhar é o SQL não deve de existir problema em partilhar.

  O programa que estou a fazer já tem a BD e quando faço o exe. ele leva essa mesma BD.

  se vou instalar por exemplo: num servidor e em 3 maquinas vai existir 3 BD ou estou enganado?

  talvez tenha que colocar a BD no servidor e os programas das 3 maquinas vão lá buscar os dados???

Obrigado mais uma vez

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bioshock

Tens razão r00tfixxxer "Tanta Confusão" por isso estou aqui a tirar duvidas e era também para aproveitar o post.

Em relação:

1º-Eu também perferia criar uma pasta no servidor, este código é para criar a pasta quando ela não existe?

2º- Podias ser mais especifico?

3º tendo em conta que a BD em que tenho estado a trabalhar é o SQL não deve de existir problema em partilhar.

  O programa que estou a fazer já tem a BD e quando faço o exe. ele leva essa mesma BD.

  se vou instalar por exemplo: num servidor e em 3 maquinas vai existir 3 BD ou estou enganado?

  talvez tenha que colocar a BD no servidor e os programas das 3 maquinas vão lá buscar os dados???

Obrigado mais uma vez

O código que ele te mostrou serve para verificar se uma determinada pasta existe, caso não exista então cria-a, caso contrário nada faz.

Quanto à última dúvida, se várias máquinas vão aceder à base de dados e se cada máquina vai interagir com a mesma base de dados então a base de dados tem que estar alojada no servidor. Portanto, quando criares o .exe só levas a aplicação no setup.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Tuntankamon

Boas

1º - Basicamente sim

2º - Primeiro partindo do pressuposto que tens noção do que é overloading, podes criar uma coluna de raiz com base na coluna datagridviewtextcolumn, tens é de implementar uma carrada de métodos.

3º - Essa da bd ser o SQL...  :D Deves ter uma bd em access, e se os dados são para serem partilhados entre os utilizadores, convêm ter só uma BD centralizada, e as três máquinas acedem  à BD. Se os dados forem independentes, podes meter uma BD por utilizador.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Bruno Damas

2º - Primeiro partindo do pressuposto que tens noção do que é overloading, podes criar uma coluna de raiz com base na coluna datagridviewtextcolumn, tens é de implementar uma carrada de métodos.

Realmente não tenho noção do que é o overloading, é a 1º vez que estou a tentar fazer isto numa coluna datagridview, será assim tão difícil?

Se não é pedir muito dá para explicar?

Em relação aquele excerto de código em que sitio o devo colocar, e tenho que o repetir quando são varias pastas, e isto ("PATH") é o nome de uma hipotética pasta? ou tem que lá estar este nome?

Desculpem mas sou um novato nestas andança.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bioshock

Verificar se a pasta "Imagens" existe na pasta bin\debug da tua aplicação, caso não exista cria-a.

If Not IO.Directory.Exists(Application.StartupPath & "\imagens") Then
            IO.Directory.CreateDirectory(Application.StartupPath & "\imagens")
End If

O código, deves-o executar quando achares necessário. Se achas que é necessário no arranque da aplicação, então metes-o no Form Load.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

Peço desculpa por me estar a meter no post.

Ando a fazer uma pequena aplicação, e ainda não estou tão avançado, mas já me deparei com algumas das dúvidas do Bruno.

Se puderem esclarecer era bom.

Em relação ao código para criar pastas, acho que percebi, engraçado como duas linhas de código fazem algo tão útil.

Mas em relação à colocação dele "no form load", não percebo, deve existir um form load em cada projecto?

ou é o evento load do primeiro form que é aberto na aplicação?

Caso os pdfs fossem para ficar na BD, qual era o tipo de dados que o armazenava (nvarchar)?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Tuntankamon

@BrunoDamas

Noção de overloading, o melhor é procurares no Google. Em relação ao criares a coluna, é um tópico um bocado avançado e para te estar aqui a explicar não saia daqui hoje. Se tiveres interesse e perceberes bem o que está em causa posso te ajudar no desenvolvimento do mesmo!  :D

@srgioslva

Form load existe em cada form dentro do teu projecto, desde que ele tenha forms é claro. E caso a tua aplicação inicie com um form, logo o primeiro load que é efectuado é o form load desse mesmo form.

Em relação aos pdfs ficarem na bd, depende da bd que utilizes mas por exemplo no sql server tens o binary/varbinary para dados até 8000 bytes, para tamanhos superiores tens um tipo que é image.

Atenção que normalmente a guardar os ficheiros numa pasta, convêm guardar o caminho (path) para o mesmo na base de dados.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Bruno Damas

O código, deves-o executar quando achares necessário. Se achas que é necessário no arranque da aplicação, então metes-o no Form Load.

Obrigado  bioshock, esta parte já entendi.

Quanto à última dúvida, se várias máquinas vão aceder à base de dados e se cada máquina vai interagir com a mesma base de dados então a base de dados tem que estar alojada no servidor. Portanto, quando criares o .exe só levas a aplicação no setup.

Em relaçao á Base Dados ela é em SQL,  mas fiquei na duvida  se basta colocar a base de dados no servidor ou tem que ser um SQL server no servidor (ou serve a simples BD que tenho) .

E mais uma duvida: quando inicio o meu programa no VB 2008 Express já dei o caminho para a BD que tenho junto do projecto, neste caso acima vou ficar com um problema quando o programa estiver a correr, os caminhos para a BD sao diferentes, (ou estarei errado).

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Bruno Damas

@BrunoDamas

Noção de overloading, o melhor é procurares no Google. Em relação ao criares a coluna, é um tópico um bocado avançado e para te estar aqui a explicar não saia daqui hoje. Se tiveres interesse e perceberes bem o que está em causa posso te ajudar no desenvolvimento do mesmo!  :D

Se por acaso tiveres algum link da net onde possa perceber isso do overloading sem te estar a maçar seria optimo, porque eu quero mesmo perceber como é que posso colocar um datetimepicker numa datagridview

Atenção que normalmente a guardar os ficheiros numa pasta, convêm guardar o caminho (path) para o mesmo na base de dados.

Obrigado pela dica

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

Muito bom tópico...

Uma dúvida,

se quiser criar uma estrutura de directórios com o código que aqui está, mas com nomes diferentes a cada ciclo de gravação de um registo no form, como é que faço?

Já percebi que se quisermos passar um endereço diferente da directoria onde estamos a trabalhar, temos que declarar variáveis do tipo string, para passarmos à função.

Mas se um dos nomes que eu queria criar era diferente (por acaso era o numero da chave primária desta tabela).

Como é que altero isso, se tenho a variável declarada.

Código do form que tenho:

Public Class frm_Carros

    Dim principalpath As String = "C:\Program Files\Microsoft Visual Studio 9.0\VB\VBProjects\Gstcarros\Gstcarros\Gstcarros"
    Dim secundarypath As String = "C:\Program Files\Microsoft Visual Studio 9.0\VB\VBProjects\Gstcarros\Gstcarros\Gstcarros\Pdfs"



    Private Sub CarrosBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CarrosBindingNavigatorSaveItem.Click
        Me.Validate()
        Me.CarroBindingSource.EndEdit()

        
        If Not IO.Directory.Exists(secundarypath & "\DocUnico") Then
            IO.Directory.CreateDirectory(secundarypath & "\DocUnico")
        End If

        If Not IO.Directory.Exists(secundarypath & "\Seguro") Then
            IO.Directory.CreateDirectory(secundarypath & "\Seguro")
        End If

        If Not IO.Directory.Exists(secundarypath & "\Inspecao") Then
            IO.Directory.CreateDirectory(secundarypath & "\Inspecao")
        End If

        Me.TableAdapterManager.UpdateAll(Me.gstcarrosdatabaseDataSet)



    End Sub

    Private Sub frm_carros_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If Not IO.Directory.Exists(principalpath & "\Pdfs") Then
            IO.Directory.CreateDirectory(principalpath & "\Pdfs")
        End If
        'TODO: This line of code loads data into the 'FieldbookdatabaseDataSet.Carros' table. You can move, or remove it, as needed.
        Me.CarrosTableAdapter.Fill(Me.gstcarrosdatabaseDataSet.Carros)

    End Sub
End Class

Isto cria-me uma directoria do seguinte tipo

Pdfs

  DocUnico

  Seguro

  Inspecao

eu queria conseguir fazer uma

Pdfs

  12345

    DocUnico

    Seguro

    Inspecao

onde o numero 12345 era a chave primaria da tabela carros e é diferente.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

A chave primária és tu que a defines, ou é auto?

A chave primária é definida pelo utilizador a cada introdução (é o numero interno do carro).

Mas se for preciso, crio uma auto, e passo esta para segundo plano.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bioshock

Se bem percebi queres criar uma pasta com o número da chave primária, correcto?

Se sim, guardas a chave primária numa variável, depois verificas se já existe o directório "Pdfs", caso exista crias a tal pasta com o nome da chave primária. Se for necessário, fazes um Else e obrigas-o a criar. (Mas já reparei que tens o código no Form Load para criar)

'Caso o directório exista, então criamos a tal pasta com a chave primária;
If IO.Directory.Exists(secundarypath) = True Then
IO.Directory.CreateDirectory(secundarypath & "\" & PegaChave)
End If

Em que PegaChave é a variável.

One more thing, se quiseres transportar a tua aplicação para outros computadores vai dar problema, a não ser que metas a aplicação na pasta exactamente igual à que definiste. Não sei se estás familiarizado com o comando Application.Startup & "\Pdfs", o que ele faz é detectar a raíz da aplicação, mais concretamente na pasta bin\debug e criar lá a pasta "Pdfs", assim escusas de andar com aquela papagaiada toda "C:\..."  :confused:

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

Bioshock, obrigado pela ajuda.

Em relação ao comando que referes, eu já tive assim, mas verifiquei que ele colocava a pasta dentro da directoria Bin\debug.

Só que eu queria-a um nível acima.

Mas secalhar é melhor fazer como dizes, fica mais genérico.

O meu problema é outro, fiz quase tudo com wizards, e agora não sei ir buscar para uma variável o valor que o utilizador está a introduzir no teclado.

Será pedir muito mais uma ajuda.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bioshock

É fácil, não tem haver com wizards. Onde é que ele insere o valor? Numa Textbox?

Se sim, exemplo:

Dim PegaChave as String
PegaChave = Textbox1.Text

Atenção que este código só deve estar quando o mesmo clicar em "OK"; "Guardar", whatever.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

Olá amigos, a parte de criação das pastas está resolvida depois de 3 noites a  :wallbash:

Como acho que pode ser interessante para alguém o que fiz, vou colocar aqui o código que cria uma estrutura de directorias deste tipo:

Documentos anexos

Pdfs das viaturas1,2,3…  (onde o numero é mutável e corresponde à chave primaria).

Documento Único

Certificado Seguro

Certificado de Inspecção

Onde: Documentos anexos é uma pasta criada no 1º form a abrir para ser usada par armazenar todos os documentos que queira guardar na aplicação.

'copia o valor da chave para a variavel Numcarro
        Numcarro = N_carroTextBox.Text

        'verifica se existe a pasta
        If Not IO.Directory.Exists(path_docs_anexos & "\Pdfs da Viatura" & Numcarro) Then
            
            'cria a estrutura do directorio
            IO.Directory.CreateDirectory(path_docs_anexos & "\Pdfs da Viatura" & Numcarro)
            IO.Directory.CreateDirectory(path_docs_anexos & "\Pdfs da Viatura" & Numcarro & "\Documento Único")
            IO.Directory.CreateDirectory(path_docs_anexos & "\Pdfs da Viatura" & Numcarro & "\Certificado de Seguro")
            IO.Directory.CreateDirectory(path_docs_anexos & "\Pdfs da Viatura" & Numcarro & "\Certificado de Inspecção")
          
        End If

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
srgioslva

Agora um novo problema, afinal a programação é assim, desafios constantes...

Consegui por um botão no form a abrir a pasta correspondente.

Ou seja a pasta do documento único, do certificado de inspecção ou do de seguro.

O problema é que abre a pasta no modo em que se selecciona o ficheiro, tipo para fazer um upload ou algo do genéro, aliás se carregarmos com o botão do lado esquerdo do rato, em cima do ficheiro que está dentro da pasta, a primeira acção, ou seja a que o windows faz quando carregamos no ficheiro é (seleccionar), e eu queria (abrir).

Já tentei utilizar outros comandos ou o shell, com uma instrução que encontrei aqui no fórum, mas não dá, já verifiquei todas as opções do comando openfiledialog, mas não encontro nenhuma que acho que resolva.

Alguém me pode dizer o que estou a fazer mal...


'abre o explorador do windows na pasta selecionada no path
        Dim OpenFileDialog4 As New OpenFileDialog
        OpenFileDialog4.InitialDirectory = path_docs_anexos & "\Pdfs da Viatura" & Numviatura & "\Documento Único"
        OpenFileDialog4.ShowDialog()
        OpenFileDialog4.RestoreDirectory = True
    End Sub

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Bruno Damas

Boas Pessoal.

Srgioslva obrigado pela explicaçao de como conseguiste criar essas pasta o que para mim tambem foi mt util.

Eu andei a pesquizar na net de como poderia colocar um datetimepicker num datagridview, e realmente nao é facil, encontrei este codigo

Public Class CalendarColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New CalendarCell())
    End Sub

    Public Overrides Property CellTemplate() As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(ByVal value As DataGridViewCell)

            ' Ensure that the cell used for the template is a CalendarCell.
            If (value IsNot Nothing) AndAlso _
                Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
                Then
                Throw New InvalidCastException("Must be a CalendarCell")
            End If
            MyBase.CellTemplate = value

        End Set
    End Property

End Class

Public Class CalendarCell
    Inherits DataGridViewTextBoxCell

    Public Sub New()
        ' Use the short date format.
        Me.Style.Format = "d"
    End Sub

    Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
        ByVal initialFormattedValue As Object, _
        ByVal dataGridViewCellStyle As DataGridViewCellStyle)

        ' Set the value of the editing control to the current cell value.
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
            dataGridViewCellStyle)

        Dim ctl As CalendarEditingControl = _
            CType(DataGridView.EditingControl, CalendarEditingControl)
        ctl.Value = CType(Me.Value, DateTime)

    End Sub

    Public Overrides ReadOnly Property EditType() As Type
        Get
            ' Return the type of the editing contol that CalendarCell uses.
            Return GetType(CalendarEditingControl)
        End Get
    End Property

    Public Overrides ReadOnly Property ValueType() As Type
        Get
            ' Return the type of the value that CalendarCell contains.
            Return GetType(DateTime)
        End Get
    End Property

    Public Overrides ReadOnly Property DefaultNewRowValue() As Object
        Get
            ' Use the current date and time as the default value.
            Return DateTime.Now
        End Get
    End Property

End Class

Class CalendarEditingControl
    Inherits DateTimePicker
    Implements IDataGridViewEditingControl

    Private dataGridViewControl As DataGridView
    Private valueIsChanged As Boolean = False
    Private rowIndexNum As Integer

    Public Sub New()
        Me.Format = DateTimePickerFormat.Short
    End Sub

    Public Property EditingControlFormattedValue() As Object _
        Implements IDataGridViewEditingControl.EditingControlFormattedValue

        Get
            Return Me.Value.ToShortDateString()
        End Get

        Set(ByVal value As Object)
            If TypeOf value Is String Then
                Me.Value = DateTime.Parse(CStr(value))
            End If
        End Set

    End Property

    Public Function GetEditingControlFormattedValue(ByVal context _
        As DataGridViewDataErrorContexts) As Object _
        Implements IDataGridViewEditingControl.GetEditingControlFormattedValue

        Return Me.Value.ToShortDateString()

    End Function

    Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As _
        DataGridViewCellStyle) _
        Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl

        Me.Font = dataGridViewCellStyle.Font
        Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
        Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor

    End Sub

    Public Property EditingControlRowIndex() As Integer _
        Implements IDataGridViewEditingControl.EditingControlRowIndex

        Get
            Return rowIndexNum
        End Get
        Set(ByVal value As Integer)
            rowIndexNum = value
        End Set

    End Property

    Public Function EditingControlWantsInputKey(ByVal key As Keys, _
        ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
        Implements IDataGridViewEditingControl.EditingControlWantsInputKey

        ' Let the DateTimePicker handle the keys listed.
        Select Case key And Keys.KeyCode
            Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
                Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp

                Return True

            Case Else
                Return Not dataGridViewWantsInputKey
        End Select

    End Function

    Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
        Implements IDataGridViewEditingControl.PrepareEditingControlForEdit

        ' No preparation needs to be done.

    End Sub

    Public ReadOnly Property RepositionEditingControlOnValueChange() _
        As Boolean Implements _
        IDataGridViewEditingControl.RepositionEditingControlOnValueChange

        Get
            Return False
        End Get

    End Property

    Public Property EditingControlDataGridView() As DataGridView _
        Implements IDataGridViewEditingControl.EditingControlDataGridView

        Get
            Return dataGridViewControl
        End Get
        Set(ByVal value As DataGridView)
            dataGridViewControl = value
        End Set

    End Property

    Public Property EditingControlValueChanged() As Boolean _
        Implements IDataGridViewEditingControl.EditingControlValueChanged

        Get
            Return valueIsChanged
        End Get
        Set(ByVal value As Boolean)
            valueIsChanged = value
        End Set

    End Property

    Public ReadOnly Property EditingControlCursor() As Cursor _
        Implements IDataGridViewEditingControl.EditingPanelCursor

        Get
            Return MyBase.Cursor
        End Get

    End Property

    Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)

        ' Notify the DataGridView that the contents of the cell have changed.
        valueIsChanged = True
        Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
        MyBase.OnValueChanged(eventargs)

    End Sub

End Class

O meu problema é que mesmo assim nao consigo colocar lá o datetimepicker.

Será que alguem me pode ajudar, obrigado

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
aafmonteiro

Boas!

Então é o seguinte...

Eu sou novo nestas bandas da programação e comecei (e ainda estou no Visual Basic Express 2008) e criei um programa. Nesse mesmo programa coloquei uma menustrip com "sub-listas". E eu coloquei um comando para que ao carregar nessa "sub-lista" ele me abra um ficheiro de video ou um pdf (os videos e pdf estão guardados numa pasta na pasta bin e sub pasta debug. Até aqui tudo bem, o problema é que eu ao publicar o programa ele não me guarda esses videos e pdf, logo não os vou conseguir abrir noutro computador onde eu instalar o programa...

Alguém me pode ajudar pf?

E desculpem pela intromissão  :)

P.S.: Não estou a trabalhar com qualquer tipo de BD

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
aafmonteiro

Pode-me dizer como?

E depois ao instalar não irá instalar numa pasta diferente da que eu tenho, logo deixará de reconhecer o comando?  :)

Eu neste momento estou a usar o comando:

Process.Start ("NOME DA PASTA\VIDEO XYZ.mp4") isto porque os ficheiros que o programa vai precisar estão neste momento na pasta bin subpasta debug

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.